From 137f0d48c080145c620fab5b14d8ccd31ec7369b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Jul 2025 08:36:29 -0500 Subject: [PATCH 001/169] chore: styling for asset_viewer bottom sheet (#20006) bottom sheet styling --- .../pages/drift_recently_taken.page.dart | 6 ++- .../presentation/pages/drift_video.page.dart | 6 ++- .../asset_viewer/bottom_bar.widget.dart | 15 ------- .../asset_viewer/bottom_sheet.widget.dart | 39 +++++++++++-------- .../bottom_sheet/location_details.widget.dart | 13 ++++--- .../base_bottom_sheet.widget.dart | 6 ++- 6 files changed, 43 insertions(+), 42 deletions(-) diff --git a/mobile/lib/presentation/pages/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/drift_recently_taken.page.dart index e2972fad56..df303e7b31 100644 --- a/mobile/lib/presentation/pages/drift_recently_taken.page.dart +++ b/mobile/lib/presentation/pages/drift_recently_taken.page.dart @@ -1,9 +1,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftRecentlyTakenPage extends StatelessWidget { @@ -29,7 +31,9 @@ class DriftRecentlyTakenPage extends StatelessWidget { }, ), ], - child: const Timeline(), + child: Timeline( + appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t()), + ), ); } } diff --git a/mobile/lib/presentation/pages/drift_video.page.dart b/mobile/lib/presentation/pages/drift_video.page.dart index 488d027177..8c0e8e6911 100644 --- a/mobile/lib/presentation/pages/drift_video.page.dart +++ b/mobile/lib/presentation/pages/drift_video.page.dart @@ -1,9 +1,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftVideoPage extends StatelessWidget { @@ -27,7 +29,9 @@ class DriftVideoPage extends StatelessWidget { }, ), ], - child: const Timeline(), + child: Timeline( + appBar: MesmerizingSliverAppBar(title: 'videos'.t()), + ), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 9237c3bcdb..d35a315f48 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -3,9 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; @@ -39,7 +37,6 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), - const _EditActionButton(), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), ]; @@ -86,15 +83,3 @@ class ViewerBottomBar extends ConsumerWidget { ); } } - -class _EditActionButton extends ConsumerWidget { - const _EditActionButton(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return BaseActionButton( - iconData: Icons.tune_outlined, - label: 'edit'.t(context: context), - ); - } -} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 6695022d1e..ea6dece942 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -73,6 +73,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { expand: false, shouldCloseOnMinExtent: false, resizeOnScroll: false, + backgroundColor: context.isDarkTheme ? Colors.black : Colors.white, ); } } @@ -84,14 +85,18 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final dateTime = asset.createdAt.toLocal(); final date = DateFormat.yMMMEd(ctx.locale.toLanguageTag()).format(dateTime); final time = DateFormat.jm(ctx.locale.toLanguageTag()).format(dateTime); - return '$date$_kSeparator$time'; + final timezone = dateTime.timeZoneOffset.isNegative + ? 'UTC-${dateTime.timeZoneOffset.inHours.abs().toString().padLeft(2, '0')}:${(dateTime.timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0')}' + : 'UTC+${dateTime.timeZoneOffset.inHours.toString().padLeft(2, '0')}:${(dateTime.timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0')}'; + return '$date$_kSeparator$time $timezone'; } String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) { final height = asset.height ?? exifInfo?.height; final width = asset.width ?? exifInfo?.width; - final resolution = - (width != null && height != null) ? "$width x $height" : null; + final resolution = (width != null && height != null) + ? "${width.toInt()} x ${height.toInt()}" + : null; final fileSize = exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; @@ -150,46 +155,46 @@ class _AssetDetailBottomSheet extends ConsumerWidget { // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 16, + titleStyle: context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w600, ), ), const SheetLocationDetails(), // Details header _SheetTile( title: 'exif_bottom_sheet_details'.t(context: context), - titleStyle: context.textTheme.labelLarge, + titleStyle: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), ), // File info _SheetTile( title: asset.name, - titleStyle: context.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.w600), + titleStyle: context.textTheme.labelLarge, leading: Icon( asset.isImage ? Icons.image_outlined : Icons.videocam_outlined, - size: 30, + size: 24, color: context.textTheme.labelLarge?.color, ), subtitle: _getFileInfo(asset, exifInfo), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), + subtitleStyle: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), // Camera info if (cameraTitle != null) _SheetTile( title: cameraTitle, - titleStyle: context.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.w600), + titleStyle: context.textTheme.labelLarge, leading: Icon( Icons.camera_outlined, - size: 30, + size: 24, color: context.textTheme.labelLarge?.color, ), subtitle: _getCameraInfoSubtitle(exifInfo), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), + subtitleStyle: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart index 2bf52bd094..855328ebde 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart @@ -95,7 +95,10 @@ class _SheetLocationDetailsState extends ConsumerState { padding: const EdgeInsets.only(bottom: 16), child: Text( "exif_bottom_sheet_location".t(context: context), - style: context.textTheme.labelLarge, + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), ), ), ExifMap( @@ -109,15 +112,13 @@ class _SheetLocationDetailsState extends ConsumerState { padding: const EdgeInsets.only(bottom: 4.0), child: Text( locationName, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.labelLarge, ), ), Text( coordinates, - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(150), + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(150), ), ), ], diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index 2db8ae2b4c..e172eec03b 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -14,6 +14,7 @@ class BaseBottomSheet extends ConsumerStatefulWidget { final bool expand; final bool shouldCloseOnMinExtent; final bool resizeOnScroll; + final Color? backgroundColor; const BaseBottomSheet({ super.key, @@ -26,6 +27,7 @@ class BaseBottomSheet extends ConsumerStatefulWidget { this.expand = true, this.shouldCloseOnMinExtent = true, this.resizeOnScroll = true, + this.backgroundColor, }); @override @@ -69,8 +71,8 @@ class _BaseDraggableScrollableSheetState shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent, builder: (BuildContext context, ScrollController scrollController) { return Card( - color: context.colorScheme.surfaceContainerHigh, - surfaceTintColor: context.colorScheme.surfaceContainerHigh, + color: widget.backgroundColor ?? + context.colorScheme.surfaceContainerHigh, borderOnForeground: false, clipBehavior: Clip.antiAlias, elevation: 6.0, From 2e63b9d9518361bc1463c3b75c8a1096a9be3749 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Jul 2025 08:39:28 -0500 Subject: [PATCH 002/169] chore: add sync indicator and better album state management (#20004) * album list rerendering * sync indicator * sync indicator * fix: lint --- mobile/lib/domain/utils/background_sync.dart | 19 ++- mobile/lib/pages/common/tab_shell.page.dart | 53 ++------ .../presentation/pages/drift_album.page.dart | 75 +----------- .../providers/background_sync.provider.dart | 8 +- .../infrastructure/remote_album.provider.dart | 48 ++------ .../lib/providers/sync_status.provider.dart | 68 +++++++++++ .../widgets/common/immich_sliver_app_bar.dart | 113 ++++++++++++++++-- 7 files changed, 226 insertions(+), 158 deletions(-) create mode 100644 mobile/lib/providers/sync_status.provider.dart diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index c71f1a8315..7ab2989354 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -4,13 +4,24 @@ import 'package:immich_mobile/providers/infrastructure/sync.provider.dart'; import 'package:immich_mobile/utils/isolate.dart'; import 'package:worker_manager/worker_manager.dart'; +typedef SyncCallback = void Function(); +typedef SyncErrorCallback = void Function(String error); + class BackgroundSyncManager { + final SyncCallback? onRemoteSyncStart; + final SyncCallback? onRemoteSyncComplete; + final SyncErrorCallback? onRemoteSyncError; + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; Cancelable? _hashTask; - BackgroundSyncManager(); + BackgroundSyncManager({ + this.onRemoteSyncStart, + this.onRemoteSyncComplete, + this.onRemoteSyncError, + }); Future cancel() { final futures = []; @@ -72,10 +83,16 @@ class BackgroundSyncManager { return _syncTask!.future; } + onRemoteSyncStart?.call(); + _syncTask = runInIsolateGentle( computation: (ref) => ref.read(syncStreamServiceProvider).sync(), ); return _syncTask!.whenComplete(() { + onRemoteSyncComplete?.call(); + _syncTask = null; + }).catchError((error) { + onRemoteSyncError?.call(error.toString()); _syncTask = null; }); } diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 24efff143f..671c1a6156 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -35,42 +35,15 @@ class _TabShellPageState extends ConsumerState { Widget build(BuildContext context) { final isScreenLandscape = context.orientation == Orientation.landscape; - Widget buildIcon({required Widget icon, required bool isProcessing}) { - if (!isProcessing) return icon; - return Stack( - alignment: Alignment.center, - clipBehavior: Clip.none, - children: [ - icon, - Positioned( - right: -18, - child: SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), - ), - ), - ), - ], - ); - } - final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), icon: const Icon( Icons.photo_library_outlined, ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + selectedIcon: Icon( + Icons.photo_library, + color: context.primaryColor, ), ), NavigationDestination( @@ -88,12 +61,9 @@ class _TabShellPageState extends ConsumerState { icon: const Icon( Icons.photo_album_outlined, ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + selectedIcon: Icon( + Icons.photo_album_rounded, + color: context.primaryColor, ), ), NavigationDestination( @@ -101,12 +71,9 @@ class _TabShellPageState extends ConsumerState { icon: const Icon( Icons.space_dashboard_outlined, ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + selectedIcon: Icon( + Icons.space_dashboard_rounded, + color: context.primaryColor, ), ), ]; @@ -183,7 +150,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { // Album page if (index == 2) { - ref.read(remoteAlbumProvider.notifier).getAll(); + ref.read(remoteAlbumProvider.notifier).refresh(); } ref.read(hapticFeedbackProvider.notifier).selectionClick(); diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index d59f734c79..fbdf1ef116 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -40,7 +40,7 @@ class _DriftAlbumsPageState extends ConsumerState { // Load albums when component mounts WidgetsBinding.instance.addPostFrameCallback((_) { - ref.read(remoteAlbumProvider.notifier).getAll(); + ref.read(remoteAlbumProvider.notifier).refresh(); }); searchController.addListener(() { @@ -88,10 +88,9 @@ class _DriftAlbumsPageState extends ConsumerState { @override Widget build(BuildContext context) { - final albumState = ref.watch(remoteAlbumProvider); - final albums = albumState.filteredAlbums; - final isLoading = albumState.isLoading; - final error = albumState.error; + final albums = + ref.watch(remoteAlbumProvider.select((s) => s.filteredAlbums)); + final userId = ref.watch(currentUserProvider)?.id; return RefreshIndicator( @@ -133,14 +132,10 @@ class _DriftAlbumsPageState extends ConsumerState { ? _AlbumGrid( albums: albums, userId: userId, - isLoading: isLoading, - error: error, ) : _AlbumList( albums: albums, userId: userId, - isLoading: isLoading, - error: error, ), ], ), @@ -481,46 +476,15 @@ class _QuickSortAndViewMode extends StatelessWidget { class _AlbumList extends ConsumerWidget { const _AlbumList({ - required this.isLoading, - required this.error, required this.albums, required this.userId, }); - final bool isLoading; - final String? error; final List albums; final String? userId; @override Widget build(BuildContext context, WidgetRef ref) { - if (isLoading) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), - ), - ); - } - - if (error != null) { - return SliverToBoxAdapter( - child: Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Text( - 'Error loading albums: $error', - style: TextStyle( - color: context.colorScheme.error, - ), - ), - ), - ), - ); - } - if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( @@ -623,44 +587,13 @@ class _AlbumGrid extends StatelessWidget { const _AlbumGrid({ required this.albums, required this.userId, - required this.isLoading, - required this.error, }); final List albums; final String? userId; - final bool isLoading; - final String? error; @override Widget build(BuildContext context) { - if (isLoading) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), - ), - ); - } - - if (error != null) { - return SliverToBoxAdapter( - child: Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Text( - 'Error loading albums: $error', - style: TextStyle( - color: context.colorScheme.error, - ), - ), - ), - ), - ); - } - if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index 83d103bb3b..dc9cc0d59f 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -1,8 +1,14 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; final backgroundSyncProvider = Provider((ref) { - final manager = BackgroundSyncManager(); + final syncStatusNotifier = ref.read(syncStatusProvider.notifier); + final manager = BackgroundSyncManager( + onRemoteSyncStart: syncStatusNotifier.startRemoteSync, + onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, + onRemoteSyncError: syncStatusNotifier.errorRemoteSync, + ); ref.onDispose(manager.cancel); return manager; }); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index 14badd58ed..2ce10d7cbd 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'album.provider.dart'; @@ -13,33 +14,25 @@ import 'album.provider.dart'; class RemoteAlbumState { final List albums; final List filteredAlbums; - final bool isLoading; - final String? error; const RemoteAlbumState({ required this.albums, List? filteredAlbums, - this.isLoading = false, - this.error, }) : filteredAlbums = filteredAlbums ?? albums; RemoteAlbumState copyWith({ List? albums, List? filteredAlbums, - bool? isLoading, - String? error, }) { return RemoteAlbumState( albums: albums ?? this.albums, filteredAlbums: filteredAlbums ?? this.filteredAlbums, - isLoading: isLoading ?? this.isLoading, - error: error ?? this.error, ); } @override String toString() => - 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length}, isLoading: $isLoading, error: $error)'; + 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length})'; @override bool operator ==(covariant RemoteAlbumState other) { @@ -47,47 +40,38 @@ class RemoteAlbumState { final listEquals = const DeepCollectionEquality().equals; return listEquals(other.albums, albums) && - listEquals(other.filteredAlbums, filteredAlbums) && - other.isLoading == isLoading && - other.error == error; + listEquals(other.filteredAlbums, filteredAlbums); } @override - int get hashCode => - albums.hashCode ^ - filteredAlbums.hashCode ^ - isLoading.hashCode ^ - error.hashCode; + int get hashCode => albums.hashCode ^ filteredAlbums.hashCode; } class RemoteAlbumNotifier extends Notifier { late RemoteAlbumService _remoteAlbumService; - + final _logger = Logger('RemoteAlbumNotifier'); @override RemoteAlbumState build() { _remoteAlbumService = ref.read(remoteAlbumServiceProvider); return const RemoteAlbumState(albums: [], filteredAlbums: []); } - Future> getAll() async { - state = state.copyWith(isLoading: true, error: null); - + Future> _getAll() async { try { final albums = await _remoteAlbumService.getAll(); state = state.copyWith( albums: albums, filteredAlbums: albums, - isLoading: false, ); return albums; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to fetch albums', error, stack); rethrow; } } Future refresh() async { - await getAll(); + await _getAll(); } void searchAlbums( @@ -127,8 +111,6 @@ class RemoteAlbumNotifier extends Notifier { String? description, List assetIds = const [], }) async { - state = state.copyWith(isLoading: true, error: null); - try { final album = await _remoteAlbumService.createAlbum( title: title, @@ -141,10 +123,9 @@ class RemoteAlbumNotifier extends Notifier { filteredAlbums: [...state.filteredAlbums, album], ); - state = state.copyWith(isLoading: false); return album; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to create album', error, stack); rethrow; } } @@ -157,8 +138,6 @@ class RemoteAlbumNotifier extends Notifier { bool? isActivityEnabled, AlbumAssetOrder? order, }) async { - state = state.copyWith(isLoading: true, error: null); - try { final updatedAlbum = await _remoteAlbumService.updateAlbum( albumId, @@ -180,12 +159,11 @@ class RemoteAlbumNotifier extends Notifier { state = state.copyWith( albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums, - isLoading: false, ); return updatedAlbum; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to update album', error, stack); rethrow; } } diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart new file mode 100644 index 0000000000..18d851aa19 --- /dev/null +++ b/mobile/lib/providers/sync_status.provider.dart @@ -0,0 +1,68 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +enum SyncStatus { + idle, + syncing, + success, + error, +} + +class SyncStatusState { + final SyncStatus remoteSyncStatus; + final String? errorMessage; + + const SyncStatusState({ + this.remoteSyncStatus = SyncStatus.idle, + this.errorMessage, + }); + + SyncStatusState copyWith({ + SyncStatus? remoteSyncStatus, + String? errorMessage, + }) { + return SyncStatusState( + remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus, + errorMessage: errorMessage ?? this.errorMessage, + ); + } + + bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is SyncStatusState && + other.remoteSyncStatus == remoteSyncStatus && + other.errorMessage == errorMessage; + } + + @override + int get hashCode => Object.hash(remoteSyncStatus, errorMessage); +} + +class SyncStatusNotifier extends Notifier { + @override + SyncStatusState build() { + return const SyncStatusState( + errorMessage: null, + remoteSyncStatus: SyncStatus.idle, + ); + } + + void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith( + remoteSyncStatus: status, + errorMessage: status == SyncStatus.error ? errorMessage : null, + ); + } + + void startRemoteSync() => setRemoteSyncStatus(SyncStatus.syncing); + void completeRemoteSync() => setRemoteSyncStatus(SyncStatus.success); + void errorRemoteSync(String error) => + setRemoteSyncStatus(SyncStatus.error, error); +} + +final syncStatusProvider = + NotifierProvider( + SyncStatusNotifier.new, +); diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 5ab60c913c..b58a1ad6f9 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -59,13 +60,6 @@ class ImmichSliverAppBar extends ConsumerWidget { centerTitle: false, title: title ?? const _ImmichLogoWithText(), actions: [ - if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), if (isCasting) Padding( padding: const EdgeInsets.only(right: 12), @@ -81,6 +75,14 @@ class ImmichSliverAppBar extends ConsumerWidget { ), ), ), + const _SyncStatusIndicator(), + if (actions != null) + ...actions!.map( + (action) => Padding( + padding: const EdgeInsets.only(right: 16), + child: action, + ), + ), if (showUploadButton) const Padding( padding: EdgeInsets.only(right: 20), @@ -273,3 +275,100 @@ class _BackupIndicator extends ConsumerWidget { return null; } } + +class _SyncStatusIndicator extends ConsumerStatefulWidget { + const _SyncStatusIndicator(); + + @override + ConsumerState<_SyncStatusIndicator> createState() => + _SyncStatusIndicatorState(); +} + +class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> + with TickerProviderStateMixin { + late AnimationController _rotationController; + late AnimationController _dismissalController; + late Animation _rotationAnimation; + late Animation _dismissalAnimation; + + @override + void initState() { + super.initState(); + _rotationController = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + ); + _dismissalController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _rotationAnimation = Tween( + begin: 0.0, + end: 1.0, + ).animate(_rotationController); + _dismissalAnimation = Tween( + begin: 1.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _dismissalController, + curve: Curves.easeOutQuart, + ), + ); + } + + @override + void dispose() { + _rotationController.dispose(); + _dismissalController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final syncStatus = ref.watch(syncStatusProvider); + final isSyncing = syncStatus.isRemoteSyncing; + + // Control animations based on sync status + if (isSyncing) { + if (!_rotationController.isAnimating) { + _rotationController.repeat(); + } + _dismissalController.reset(); + } else { + _rotationController.stop(); + if (_dismissalController.status == AnimationStatus.dismissed) { + _dismissalController.forward(); + } + } + + // Don't show anything if not syncing and dismissal animation is complete + if (!isSyncing && + _dismissalController.status == AnimationStatus.completed) { + return const SizedBox.shrink(); + } + + return AnimatedBuilder( + animation: Listenable.merge([_rotationAnimation, _dismissalAnimation]), + builder: (context, child) { + return Padding( + padding: EdgeInsets.only(right: isSyncing ? 16 : 0), + child: Transform.scale( + scale: isSyncing ? 1.0 : _dismissalAnimation.value, + child: Opacity( + opacity: isSyncing ? 1.0 : _dismissalAnimation.value, + child: Transform.rotate( + angle: _rotationAnimation.value * 2 * 3.14159, + child: Icon( + Icons.sync, + size: 24, + color: context.primaryColor, + ), + ), + ), + ), + ); + }, + ); + } +} From 7bae49ebd51e0c4d465f1f9c9d00af02481da205 Mon Sep 17 00:00:00 2001 From: Daimolean <92239625+wuzihao051119@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:21:39 +0800 Subject: [PATCH 003/169] feat(mobile): people sync (#19777) * feat(mobile): drift people sync * merge main --------- Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v1.json | 2 +- .../drift_schemas/main/drift_schema_v2.json | 2 +- mobile/lib/domain/models/memory.model.dart | 16 +- mobile/lib/domain/models/person.model.dart | 125 ++- mobile/lib/domain/models/stack.model.dart | 35 +- .../domain/services/sync_stream.service.dart | 4 + .../entities/person.entity.dart | 34 + .../entities/person.entity.drift.dart | 933 ++++++++++++++++++ .../repositories/db.repository.dart | 2 + .../repositories/db.repository.drift.dart | 23 +- .../repositories/db.repository.steps.dart | 74 ++ .../repositories/person.repository.dart | 36 + .../repositories/sync_api.repository.dart | 3 + .../repositories/sync_stream.repository.dart | 43 + .../models/search/search_filter.model.dart | 4 +- mobile/lib/pages/search/search.page.dart | 2 +- .../pages/dev/feat_in_development.page.dart | 1 + .../pages/dev/media_stat.page.dart | 4 + .../pages/search/drift_search.page.dart | 2 +- .../infrastructure/person.provider.dart | 7 + .../{ => infrastructure}/stack.provider.dart | 0 .../lib/providers/search/people.provider.dart | 2 +- .../providers/search/people.provider.g.dart | 7 +- mobile/lib/repositories/auth.repository.dart | 1 + .../repositories/person_api.repository.dart | 6 +- mobile/lib/services/person.service.dart | 4 +- .../search/search_filter/people_picker.dart | 6 +- .../services/sync_stream_service_test.dart | 4 + .../test/drift/main/generated/schema_v1.dart | 439 +++++++- .../test/drift/main/generated/schema_v2.dart | 439 +++++++- 30 files changed, 2197 insertions(+), 63 deletions(-) create mode 100644 mobile/lib/infrastructure/entities/person.entity.dart create mode 100644 mobile/lib/infrastructure/entities/person.entity.drift.dart create mode 100644 mobile/lib/infrastructure/repositories/person.repository.dart create mode 100644 mobile/lib/providers/infrastructure/person.provider.dart rename mobile/lib/providers/{ => infrastructure}/stack.provider.dart (100%) diff --git a/mobile/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json index c19bcfb945..978a9ba8ad 100644 --- a/mobile/drift_schemas/main/drift_schema_v1.json +++ b/mobile/drift_schemas/main/drift_schema_v1.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v2.json b/mobile/drift_schemas/main/drift_schema_v2.json index c19bcfb945..978a9ba8ad 100644 --- a/mobile/drift_schemas/main/drift_schema_v2.json +++ b/mobile/drift_schemas/main/drift_schema_v2.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index ba2a43428f..38d1c7ef7d 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -124,7 +124,21 @@ class DriftMemory { @override String toString() { - return 'Memory(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, ownerId: $ownerId, type: $type, data: $data, isSaved: $isSaved, memoryAt: $memoryAt, seenAt: $seenAt, showAt: $showAt, hideAt: $hideAt, assets: $assets)'; + return '''Memory { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + deletedAt: ${deletedAt ?? ""}, + ownerId: $ownerId, + type: $type, + data: $data, + isSaved: $isSaved, + memoryAt: $memoryAt, + seenAt: ${seenAt ?? ""}, + showAt: ${showAt ?? ""}, + hideAt: ${hideAt ?? ""}, + assets: $assets +}'''; } @override diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index 10453f768d..d9eee9ae06 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -1,7 +1,8 @@ import 'dart:convert'; -class Person { - const Person({ +// TODO: Remove PersonDto once Isar is removed +class PersonDto { + const PersonDto({ required this.id, this.birthDate, required this.isHidden, @@ -22,7 +23,7 @@ class Person { return 'Person(id: $id, birthDate: $birthDate, isHidden: $isHidden, name: $name, thumbnailPath: $thumbnailPath, updatedAt: $updatedAt)'; } - Person copyWith({ + PersonDto copyWith({ String? id, DateTime? birthDate, bool? isHidden, @@ -30,7 +31,7 @@ class Person { String? thumbnailPath, DateTime? updatedAt, }) { - return Person( + return PersonDto( id: id ?? this.id, birthDate: birthDate ?? this.birthDate, isHidden: isHidden ?? this.isHidden, @@ -51,8 +52,8 @@ class Person { }; } - factory Person.fromMap(Map map) { - return Person( + factory PersonDto.fromMap(Map map) { + return PersonDto( id: map['id'] as String, birthDate: map['birthDate'] != null ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) @@ -68,11 +69,11 @@ class Person { String toJson() => json.encode(toMap()); - factory Person.fromJson(String source) => - Person.fromMap(json.decode(source) as Map); + factory PersonDto.fromJson(String source) => + PersonDto.fromMap(json.decode(source) as Map); @override - bool operator ==(covariant Person other) { + bool operator ==(covariant PersonDto other) { if (identical(this, other)) return true; return other.id == id && @@ -93,3 +94,109 @@ class Person { updatedAt.hashCode; } } + +// Model for a person stored in the server +class Person { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + + const Person({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + required this.color, + this.birthDate, + }); + + Person copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + String? faceAssetId, + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + String? color, + DateTime? birthDate, + }) { + return Person( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + String toString() { + return '''Person { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + ownerId: $ownerId, + name: $name, + faceAssetId: ${faceAssetId ?? ""}, + thumbnailPath: $thumbnailPath, + isFavorite: $isFavorite, + isHidden: $isHidden, + color: ${color ?? ""}, + birthDate: ${birthDate ?? ""} +}'''; + } + + @override + bool operator ==(covariant Person other) { + if (identical(this, other)) return true; + + return other.id == id && + other.createdAt == createdAt && + other.updatedAt == updatedAt && + other.ownerId == ownerId && + other.name == name && + other.faceAssetId == faceAssetId && + other.thumbnailPath == thumbnailPath && + other.isFavorite == isFavorite && + other.isHidden == isHidden && + other.color == color && + other.birthDate == birthDate; + } + + @override + int get hashCode { + return id.hashCode ^ + createdAt.hashCode ^ + updatedAt.hashCode ^ + ownerId.hashCode ^ + name.hashCode ^ + faceAssetId.hashCode ^ + thumbnailPath.hashCode ^ + isFavorite.hashCode ^ + isHidden.hashCode ^ + color.hashCode ^ + birthDate.hashCode; + } +} diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index d7faf07a22..6a449a1eb8 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - // Model for a stack stored in the server class Stack { final String id; @@ -32,34 +30,15 @@ class Stack { ); } - Map toMap() { - return { - 'id': id, - 'createdAt': createdAt.millisecondsSinceEpoch, - 'updatedAt': updatedAt.millisecondsSinceEpoch, - 'ownerId': ownerId, - 'primaryAssetId': primaryAssetId, - }; - } - - factory Stack.fromMap(Map map) { - return Stack( - id: map['id'] as String, - createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int), - updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int), - ownerId: map['ownerId'] as String, - primaryAssetId: map['primaryAssetId'] as String, - ); - } - - String toJson() => json.encode(toMap()); - - factory Stack.fromJson(String source) => - Stack.fromMap(json.decode(source) as Map); - @override String toString() { - return 'Stack(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, ownerId: $ownerId, primaryAssetId: $primaryAssetId)'; + return '''Stack { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + ownerId: $ownerId, + primaryAssetId: $primaryAssetId +}'''; } @override diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 6183865041..9a7d91ced9 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -240,6 +240,10 @@ class SyncStreamService { return _syncStreamRepository.deleteUserMetadatasV1( data.cast(), ); + case SyncEntityType.personV1: + return _syncStreamRepository.updatePeopleV1(data.cast()); + case SyncEntityType.personDeleteV1: + return _syncStreamRepository.deletePeopleV1(data.cast()); default: _logger.warning("Unknown sync data type: $type"); } diff --git a/mobile/lib/infrastructure/entities/person.entity.dart b/mobile/lib/infrastructure/entities/person.entity.dart new file mode 100644 index 0000000000..68dd04cb5f --- /dev/null +++ b/mobile/lib/infrastructure/entities/person.entity.dart @@ -0,0 +1,34 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class PersonEntity extends Table with DriftDefaultsMixin { + const PersonEntity(); + + TextColumn get id => text()(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + TextColumn get ownerId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get name => text()(); + + // TODO: foreign key refering to asset faces + TextColumn get faceAssetId => text().nullable()(); + + TextColumn get thumbnailPath => text()(); + + BoolColumn get isFavorite => boolean()(); + + BoolColumn get isHidden => boolean()(); + + TextColumn get color => text().nullable()(); + + DateTimeColumn get birthDate => dateTime().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart new file mode 100644 index 0000000000..f0ced63f0e --- /dev/null +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -0,0 +1,933 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart' as i2; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i4; +import 'package:drift/internal/modular.dart' as i5; + +typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion + Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String name, + i0.Value faceAssetId, + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + i0.Value color, + i0.Value birthDate, +}); +typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion + Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value name, + i0.Value faceAssetId, + i0.Value thumbnailPath, + i0.Value isFavorite, + i0.Value isHidden, + i0.Value color, + i0.Value birthDate, +}); + +final class $$PersonEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$PersonEntityTable, i1.PersonEntityData> { + $$PersonEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => + i5.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer(db) + .resultSet('person_entity') + .ownerId, + i5.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i4.$$UserEntityTableProcessedTableManager get ownerId { + final $_column = $_itemColumn('owner_id')!; + + final manager = i4 + .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$PersonEntityTableFilterComposer + extends i0.Composer { + $$PersonEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get name => $composableBuilder( + column: $table.name, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get faceAssetId => $composableBuilder( + column: $table.faceAssetId, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get thumbnailPath => $composableBuilder( + column: $table.thumbnailPath, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get isFavorite => $composableBuilder( + column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get isHidden => $composableBuilder( + column: $table.isHidden, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get color => $composableBuilder( + column: $table.color, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get birthDate => $composableBuilder( + column: $table.birthDate, builder: (column) => i0.ColumnFilters(column)); + + i4.$$UserEntityTableFilterComposer get ownerId { + final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PersonEntityTableOrderingComposer + extends i0.Composer { + $$PersonEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get name => $composableBuilder( + column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get faceAssetId => $composableBuilder( + column: $table.faceAssetId, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get thumbnailPath => $composableBuilder( + column: $table.thumbnailPath, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get isFavorite => $composableBuilder( + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get isHidden => $composableBuilder( + column: $table.isHidden, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get color => $composableBuilder( + column: $table.color, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get birthDate => $composableBuilder( + column: $table.birthDate, + builder: (column) => i0.ColumnOrderings(column)); + + i4.$$UserEntityTableOrderingComposer get ownerId { + final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PersonEntityTableAnnotationComposer + extends i0.Composer { + $$PersonEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i0.GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + i0.GeneratedColumn get faceAssetId => $composableBuilder( + column: $table.faceAssetId, builder: (column) => column); + + i0.GeneratedColumn get thumbnailPath => $composableBuilder( + column: $table.thumbnailPath, builder: (column) => column); + + i0.GeneratedColumn get isFavorite => $composableBuilder( + column: $table.isFavorite, builder: (column) => column); + + i0.GeneratedColumn get isHidden => + $composableBuilder(column: $table.isHidden, builder: (column) => column); + + i0.GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); + + i0.GeneratedColumn get birthDate => + $composableBuilder(column: $table.birthDate, builder: (column) => column); + + i4.$$UserEntityTableAnnotationComposer get ownerId { + final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PersonEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId})> { + $$PersonEntityTableTableManager( + i0.GeneratedDatabase db, i1.$PersonEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$PersonEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$PersonEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$PersonEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value faceAssetId = const i0.Value.absent(), + i0.Value thumbnailPath = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value isHidden = const i0.Value.absent(), + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => + i1.PersonEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + thumbnailPath: thumbnailPath, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + createCompanionCallback: ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + i0.Value faceAssetId = const i0.Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => + i1.PersonEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + thumbnailPath: thumbnailPath, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$PersonEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({ownerId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (ownerId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: + i1.$$PersonEntityTableReferences._ownerIdTable(db), + referencedColumn: + i1.$$PersonEntityTableReferences._ownerIdTable(db).id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$PersonEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId})>; + +class $PersonEntityTable extends i2.PersonEntity + with i0.TableInfo<$PersonEntityTable, i1.PersonEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $PersonEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _createdAtMeta = + const i0.VerificationMeta('createdAt'); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn('created_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime); + static const i0.VerificationMeta _updatedAtMeta = + const i0.VerificationMeta('updatedAt'); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn('updated_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime); + static const i0.VerificationMeta _ownerIdMeta = + const i0.VerificationMeta('ownerId'); + @override + late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( + 'owner_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _nameMeta = + const i0.VerificationMeta('name'); + @override + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _faceAssetIdMeta = + const i0.VerificationMeta('faceAssetId'); + @override + late final i0.GeneratedColumn faceAssetId = + i0.GeneratedColumn('face_asset_id', aliasedName, true, + type: i0.DriftSqlType.string, requiredDuringInsert: false); + static const i0.VerificationMeta _thumbnailPathMeta = + const i0.VerificationMeta('thumbnailPath'); + @override + late final i0.GeneratedColumn thumbnailPath = + i0.GeneratedColumn('thumbnail_path', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _isFavoriteMeta = + const i0.VerificationMeta('isFavorite'); + @override + late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( + 'is_favorite', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + static const i0.VerificationMeta _isHiddenMeta = + const i0.VerificationMeta('isHidden'); + @override + late final i0.GeneratedColumn isHidden = i0.GeneratedColumn( + 'is_hidden', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))')); + static const i0.VerificationMeta _colorMeta = + const i0.VerificationMeta('color'); + @override + late final i0.GeneratedColumn color = i0.GeneratedColumn( + 'color', aliasedName, true, + type: i0.DriftSqlType.string, requiredDuringInsert: false); + static const i0.VerificationMeta _birthDateMeta = + const i0.VerificationMeta('birthDate'); + @override + late final i0.GeneratedColumn birthDate = + i0.GeneratedColumn('birth_date', aliasedName, true, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + } + if (data.containsKey('owner_id')) { + context.handle(_ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + } else if (isInserting) { + context.missing(_ownerIdMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('face_asset_id')) { + context.handle( + _faceAssetIdMeta, + faceAssetId.isAcceptableOrUnknown( + data['face_asset_id']!, _faceAssetIdMeta)); + } + if (data.containsKey('thumbnail_path')) { + context.handle( + _thumbnailPathMeta, + thumbnailPath.isAcceptableOrUnknown( + data['thumbnail_path']!, _thumbnailPathMeta)); + } else if (isInserting) { + context.missing(_thumbnailPathMeta); + } + if (data.containsKey('is_favorite')) { + context.handle( + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown( + data['is_favorite']!, _isFavoriteMeta)); + } else if (isInserting) { + context.missing(_isFavoriteMeta); + } + if (data.containsKey('is_hidden')) { + context.handle(_isHiddenMeta, + isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta)); + } else if (isInserting) { + context.missing(_isHiddenMeta); + } + if (data.containsKey('color')) { + context.handle( + _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + } + if (data.containsKey('birth_date')) { + context.handle(_birthDateMeta, + birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.PersonEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + thumbnailPath: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, + isFavorite: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + $PersonEntityTable createAlias(String alias) { + return $PersonEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['created_at'] = i0.Variable(createdAt); + map['updated_at'] = i0.Variable(updatedAt); + map['owner_id'] = i0.Variable(ownerId); + map['name'] = i0.Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = i0.Variable(faceAssetId); + } + map['thumbnail_path'] = i0.Variable(thumbnailPath); + map['is_favorite'] = i0.Variable(isFavorite); + map['is_hidden'] = i0.Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = i0.Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = i0.Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + i1.PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + i0.Value faceAssetId = const i0.Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent()}) => + i1.PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(i1.PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value createdAt; + final i0.Value updatedAt; + final i0.Value ownerId; + final i0.Value name; + final i0.Value faceAssetId; + final i0.Value thumbnailPath; + final i0.Value isFavorite; + final i0.Value isHidden; + final i0.Value color; + final i0.Value birthDate; + const PersonEntityCompanion({ + this.id = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.ownerId = const i0.Value.absent(), + this.name = const i0.Value.absent(), + this.faceAssetId = const i0.Value.absent(), + this.thumbnailPath = const i0.Value.absent(), + this.isFavorite = const i0.Value.absent(), + this.isHidden = const i0.Value.absent(), + this.color = const i0.Value.absent(), + this.birthDate = const i0.Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const i0.Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const i0.Value.absent(), + this.birthDate = const i0.Value.absent(), + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + name = i0.Value(name), + thumbnailPath = i0.Value(thumbnailPath), + isFavorite = i0.Value(isFavorite), + isHidden = i0.Value(isHidden); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? createdAt, + i0.Expression? updatedAt, + i0.Expression? ownerId, + i0.Expression? name, + i0.Expression? faceAssetId, + i0.Expression? thumbnailPath, + i0.Expression? isFavorite, + i0.Expression? isHidden, + i0.Expression? color, + i0.Expression? birthDate, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + i1.PersonEntityCompanion copyWith( + {i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? name, + i0.Value? faceAssetId, + i0.Value? thumbnailPath, + i0.Value? isFavorite, + i0.Value? isHidden, + i0.Value? color, + i0.Value? birthDate}) { + return i1.PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = i0.Variable(ownerId.value); + } + if (name.present) { + map['name'] = i0.Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = i0.Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = i0.Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = i0.Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = i0.Variable(isHidden.value); + } + if (color.present) { + map['color'] = i0.Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = i0.Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 200ec47516..0a763c91cc 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; @@ -54,6 +55,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { MemoryEntity, MemoryAssetEntity, StackEntity, + PersonEntity, ], include: { 'package:immich_mobile/infrastructure/entities/merged_asset.drift', diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 3b826c209b..0f822e57eb 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -29,9 +29,11 @@ import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i13; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' as i14; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i15; -import 'package:drift/internal/modular.dart' as i16; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i16; +import 'package:drift/internal/modular.dart' as i17; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); @@ -61,8 +63,9 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this); late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14.$MemoryAssetEntityTable(this); - i15.MergedAssetDrift get mergedAssetDrift => i16.ReadDatabaseContainer(this) - .accessor(i15.MergedAssetDrift.new); + late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); + i16.MergedAssetDrift get mergedAssetDrift => i17.ReadDatabaseContainer(this) + .accessor(i16.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -84,7 +87,8 @@ abstract class $Drift extends i0.GeneratedDatabase { remoteAlbumAssetEntity, remoteAlbumUserEntity, memoryEntity, - memoryAssetEntity + memoryAssetEntity, + personEntity ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => @@ -216,6 +220,13 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), + ], + ), ], ); @override @@ -255,4 +266,6 @@ class $DriftManager { i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity); i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity => i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); + i15.$$PersonEntityTableTableManager get personEntity => + i15.$$PersonEntityTableTableManager(_db, _db.personEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 4b27ce830c..76e5df76d0 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -26,6 +26,7 @@ final class Schema2 extends i0.VersionedSchema { remoteAlbumUserEntity, memoryEntity, memoryAssetEntity, + personEntity, ]; late final Shape0 userEntity = Shape0( source: i0.VersionedTable( @@ -321,6 +322,30 @@ final class Schema2 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); + late final Shape13 personEntity = Shape13( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null); } class Shape0 extends i0.VersionedTable { @@ -845,6 +870,55 @@ i1.GeneratedColumn _column_68(String aliasedName) => type: i1.DriftSqlType.string, defaultConstraints: i1.GeneratedColumn.constraintIsAlways( 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + +class Shape13 extends i0.VersionedTable { + Shape13({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailPath => + columnsByName['thumbnail_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_69(String aliasedName) => + i1.GeneratedColumn('face_asset_id', aliasedName, true, + type: i1.DriftSqlType.string); +i1.GeneratedColumn _column_70(String aliasedName) => + i1.GeneratedColumn('thumbnail_path', aliasedName, false, + type: i1.DriftSqlType.string); +i1.GeneratedColumn _column_71(String aliasedName) => + i1.GeneratedColumn('is_favorite', aliasedName, false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); +i1.GeneratedColumn _column_72(String aliasedName) => + i1.GeneratedColumn('is_hidden', aliasedName, false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))')); +i1.GeneratedColumn _column_73(String aliasedName) => + i1.GeneratedColumn('color', aliasedName, true, + type: i1.DriftSqlType.string); +i1.GeneratedColumn _column_74(String aliasedName) => + i1.GeneratedColumn('birth_date', aliasedName, true, + type: i1.DriftSqlType.dateTime); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, }) { diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart new file mode 100644 index 0000000000..859765d63b --- /dev/null +++ b/mobile/lib/infrastructure/repositories/person.repository.dart @@ -0,0 +1,36 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPersonRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPersonRepository(this._db) : super(_db); + + Future> getAll(String userId) { + final query = _db.personEntity.select() + ..where((e) => e.ownerId.equals(userId)); + + return query.map((person) { + return person.toDto(); + }).get(); + } +} + +extension on PersonEntityData { + Person toDto() { + return Person( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + thumbnailPath: thumbnailPath, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index d1ecbd580c..11d58663e0 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -57,6 +57,7 @@ class SyncApiRepository { SyncRequestType.stacksV1, SyncRequestType.partnerStacksV1, SyncRequestType.userMetadataV1, + SyncRequestType.peopleV1, ], ).toJson(), ); @@ -173,6 +174,8 @@ const _kResponseMap = { SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson, SyncEntityType.userMetadataV1: SyncUserMetadataV1.fromJson, SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson, + SyncEntityType.personV1: SyncPersonV1.fromJson, + SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson, }; class _SyncAckV1 { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index d28c68ed78..fb5c7fdb3c 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'; @@ -511,6 +512,48 @@ class SyncStreamRepository extends DriftDatabaseRepository { rethrow; } } + + Future updatePeopleV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final person in data) { + final companion = PersonEntityCompanion( + createdAt: Value(person.createdAt), + updatedAt: Value(person.updatedAt), + ownerId: Value(person.ownerId), + name: Value(person.name), + faceAssetId: Value(person.faceAssetId), + thumbnailPath: Value(person.thumbnailPath), + isFavorite: Value(person.isFavorite), + isHidden: Value(person.isHidden), + color: Value(person.color), + birthDate: Value(person.birthDate), + ); + + batch.insert( + _db.personEntity, + companion.copyWith(id: Value(person.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updatePeopleV1', error, stack); + rethrow; + } + } + + Future deletePeopleV1( + Iterable data, + ) async { + try { + await _db.personEntity.deleteWhere( + (row) => row.id.isIn(data.map((e) => e.personId)), + ); + } catch (error, stack) { + _logger.severe('Error: deletePeopleV1', error, stack); + } + } } extension on AssetTypeEnum { diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 835e6aff8f..efe6f923ad 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -237,7 +237,7 @@ class SearchFilter { String? filename; String? description; String? language; - Set people; + Set people; SearchLocationFilter location; SearchCameraFilter camera; SearchDateFilter date; @@ -282,7 +282,7 @@ class SearchFilter { String? filename, String? description, String? language, - Set? people, + Set? people, SearchLocationFilter? location, SearchCameraFilter? camera, SearchDateFilter? date, diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index dba3199fac..3e5c153f88 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -147,7 +147,7 @@ class SearchPage extends HookConsumerWidget { ); showPeoplePicker() { - handleOnSelect(Set value) { + handleOnSelect(Set value) { filter.value = filter.value.copyWith( people: value, ); diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 2815b2db83..e94329acd2 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -112,6 +112,7 @@ final _features = [ await db.memoryEntity.deleteAll(); await db.memoryAssetEntity.deleteAll(); await db.stackEntity.deleteAll(); + await db.personEntity.deleteAll(); }, ), _Feature( diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index e61dcdf90d..acd7b219b3 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -166,6 +166,10 @@ final _remoteStats = [ name: 'Stacks', load: (db) => db.managers.stackEntity.count(), ), + _Stat( + name: 'People', + load: (db) => db.managers.personEntity.count(), + ), ]; @RoutePage() diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 5a5eb44b3b..7101a42b01 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -151,7 +151,7 @@ class DriftSearchPage extends HookConsumerWidget { ); showPeoplePicker() { - handleOnSelect(Set value) { + handleOnSelect(Set value) { filter.value = filter.value.copyWith( people: value, ); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart new file mode 100644 index 0000000000..a733104b33 --- /dev/null +++ b/mobile/lib/providers/infrastructure/person.provider.dart @@ -0,0 +1,7 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; + +final driftPersonProvider = Provider( + (ref) => DriftPersonRepository(ref.watch(driftProvider)), +); diff --git a/mobile/lib/providers/stack.provider.dart b/mobile/lib/providers/infrastructure/stack.provider.dart similarity index 100% rename from mobile/lib/providers/stack.provider.dart rename to mobile/lib/providers/infrastructure/stack.provider.dart diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index d03d533aaf..f6ac9d1125 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -9,7 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'people.provider.g.dart'; @riverpod -Future> getAllPeople( +Future> getAllPeople( Ref ref, ) async { final PersonService personService = ref.read(personServiceProvider); diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart index 391edd362c..4625891abb 100644 --- a/mobile/lib/providers/search/people.provider.g.dart +++ b/mobile/lib/providers/search/people.provider.g.dart @@ -6,11 +6,12 @@ part of 'people.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$getAllPeopleHash() => r'226947af3b09ce62224916543958dd1d5e2ba651'; +String _$getAllPeopleHash() => r'2c5e6a207683f15ab209650615fdf9cb7f76c736'; /// See also [getAllPeople]. @ProviderFor(getAllPeople) -final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( +final getAllPeopleProvider = + AutoDisposeFutureProvider>.internal( getAllPeople, name: r'getAllPeopleProvider', debugGetCreateSourceHash: @@ -21,7 +22,7 @@ final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef GetAllPeopleRef = AutoDisposeFutureProviderRef>; +typedef GetAllPeopleRef = AutoDisposeFutureProviderRef>; String _$personAssetsHash() => r'c1d35ee0e024bd6915e21bc724be4b458a14bc24'; /// Copied from Dart SDK diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index eb86e1809f..5cf357d5a4 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -34,6 +34,7 @@ class AuthRepository extends DatabaseRepository { _drift.userMetadataEntity.deleteAll(), _drift.partnerEntity.deleteAll(), _drift.stackEntity.deleteAll(), + _drift.personEntity.deleteAll(), ]); // Drift deletions - parent entities await Future.wait([ diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index a2a6e2489b..26f11dd51d 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -13,19 +13,19 @@ class PersonApiRepository extends ApiRepository { PersonApiRepository(this._api); - Future> getAll() async { + Future> getAll() async { final dto = await checkNull(_api.getAllPeople()); return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name}) async { + Future update(String id, {String? name}) async { final dto = await checkNull( _api.updatePerson(id, PersonUpdateDto(name: name)), ); return _toPerson(dto); } - static Person _toPerson(PersonResponseDto dto) => Person( + static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( birthDate: dto.birthDate, id: dto.id, isHidden: dto.isHidden, diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index a591ad4f27..08b18dfd10 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -28,7 +28,7 @@ class PersonService { this._assetRepository, ); - Future> getAllPeople() async { + Future> getAllPeople() async { try { return await _personApiRepository.getAll(); } catch (error, stack) { @@ -48,7 +48,7 @@ class PersonService { return []; } - Future updateName(String id, String name) async { + Future updateName(String id, String name) async { try { return await _personApiRepository.update(id, name: name); } catch (error, stack) { diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index 44d01d274e..05f699b44b 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -14,8 +14,8 @@ import 'package:immich_mobile/widgets/common/search_field.dart'; class PeoplePicker extends HookConsumerWidget { const PeoplePicker({super.key, required this.onSelect, this.filter}); - final Function(Set) onSelect; - final Set? filter; + final Function(Set) onSelect; + final Set? filter; @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,7 +24,7 @@ class PeoplePicker extends HookConsumerWidget { final searchQuery = useState(''); final people = ref.watch(getAllPeopleProvider); final headers = ApiService.getRequestHeaders(); - final selectedPeople = useState>(filter ?? {}); + final selectedPeople = useState>(filter ?? {}); return Column( children: [ diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index c9fd8104e4..deb19dfcf8 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -105,6 +105,10 @@ void main() { .thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())) .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePeopleV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePeopleV1(any())) + .thenAnswer(successHandler); sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, diff --git a/mobile/test/drift/main/generated/schema_v1.dart b/mobile/test/drift/main/generated/schema_v1.dart index 38dbb9f827..d7b88ea3cf 100644 --- a/mobile/test/drift/main/generated/schema_v1.dart +++ b/mobile/test/drift/main/generated/schema_v1.dart @@ -4645,6 +4645,441 @@ class MemoryAssetEntityCompanion } } +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + thumbnailPath: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent()}) => + PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate}) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + class DatabaseAtV1 extends GeneratedDatabase { DatabaseAtV1(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); @@ -4671,6 +5106,7 @@ class DatabaseAtV1 extends GeneratedDatabase { RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -4692,7 +5128,8 @@ class DatabaseAtV1 extends GeneratedDatabase { remoteAlbumAssetEntity, remoteAlbumUserEntity, memoryEntity, - memoryAssetEntity + memoryAssetEntity, + personEntity ]; @override int get schemaVersion => 1; diff --git a/mobile/test/drift/main/generated/schema_v2.dart b/mobile/test/drift/main/generated/schema_v2.dart index 8345cef906..e3edac3501 100644 --- a/mobile/test/drift/main/generated/schema_v2.dart +++ b/mobile/test/drift/main/generated/schema_v2.dart @@ -4645,6 +4645,441 @@ class MemoryAssetEntityCompanion } } +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + thumbnailPath: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent()}) => + PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate}) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + class DatabaseAtV2 extends GeneratedDatabase { DatabaseAtV2(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); @@ -4671,6 +5106,7 @@ class DatabaseAtV2 extends GeneratedDatabase { RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -4692,7 +5128,8 @@ class DatabaseAtV2 extends GeneratedDatabase { remoteAlbumAssetEntity, remoteAlbumUserEntity, memoryEntity, - memoryAssetEntity + memoryAssetEntity, + personEntity ]; @override int get schemaVersion => 2; From f32d4f15b63112309f6b89bec73ba367f7c26f61 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Fri, 18 Jul 2025 09:37:07 -0500 Subject: [PATCH 004/169] feat(mobile): android widgets (#19310) * wip * wip widgets * more wip changes * latest changes * working random widget * cleanup * add configurable widget * add memory widget and cleanup of codebase * album name handling * add deeplinks * finish minor refactoring and add some polish :) * fix single shot type on random widget Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> * switch to ExposedDropdownMenuBox for random configure activity * handle empty album and no connection edge cases * android project cleanup * fix proguard and gson issues * fix deletion handling * fix proguard stripping for widget model classes/enums * change random configuration activity close to a checkmark on right side --------- Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> --- mobile/android/app/build.gradle | 19 ++ mobile/android/app/proguard-rules.pro | 9 +- .../android/app/src/main/AndroidManifest.xml | 38 ++- .../app/alextran/immich/widget/BitmapUtils.kt | 33 +++ .../immich/widget/ImageDownloadWorker.kt | 241 ++++++++++++++++++ .../app/alextran/immich/widget/ImmichAPI.kt | 103 ++++++++ .../alextran/immich/widget/MemoryReceiver.kt | 56 ++++ .../app/alextran/immich/widget/PhotoWidget.kt | 124 +++++++++ .../alextran/immich/widget/RandomReceiver.kt | 55 ++++ .../immich/widget/configure/Dropdown.kt | 64 +++++ .../immich/widget/configure/LightDarkTheme.kt | 28 ++ .../widget/configure/RandomConfigure.kt | 210 +++++++++++++++ .../app/alextran/immich/widget/model/Model.kt | 79 ++++++ .../res/drawable-nodpi/memory_preview.png | Bin 0 -> 246192 bytes .../res/drawable-nodpi/random_preview.png | Bin 0 -> 250040 bytes .../app/src/main/res/values/strings.xml | 8 + .../app/src/main/res/xml/memory_widget.xml | 9 + .../app/src/main/res/xml/random_widget.xml | 13 + mobile/lib/constants/constants.dart | 7 +- .../lib/repositories/widget.repository.dart | 7 +- mobile/lib/services/widget.service.dart | 7 +- 21 files changed, 1098 insertions(+), 12 deletions(-) create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt create mode 100644 mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png create mode 100644 mobile/android/app/src/main/res/drawable-nodpi/random_preview.png create mode 100644 mobile/android/app/src/main/res/values/strings.xml create mode 100644 mobile/android/app/src/main/res/xml/memory_widget.xml create mode 100644 mobile/android/app/src/main/res/xml/random_widget.xml diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 870e424461..8b4dc42b7e 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -3,6 +3,8 @@ plugins { id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" id 'com.google.devtools.ksp' + id 'org.jetbrains.kotlin.plugin.compose' version '2.0.20' // this version matches your Kotlin version + } def localProperties = new Properties() @@ -45,6 +47,10 @@ android { main.java.srcDirs += 'src/main/kotlin' } + buildFeatures { + compose true + } + defaultConfig { applicationId "app.alextran.immich" minSdkVersion 26 @@ -105,6 +111,8 @@ dependencies { def guava_version = '33.3.1-android' def glide_version = '4.16.0' def serialization_version = '1.8.1' + def compose_version = '1.1.1' + def gson_version = '2.10.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" @@ -116,6 +124,17 @@ dependencies { ksp "com.github.bumptech.glide:ksp:$glide_version" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2' + + //Glance Widget + implementation "androidx.glance:glance-appwidget:$compose_version" + implementation "com.google.code.gson:gson:$gson_version" + + // Glance Configure + implementation "androidx.activity:activity-compose:1.8.2" + implementation "androidx.compose.ui:ui:$compose_version" + implementation "androidx.compose.ui:ui-tooling:$compose_version" + implementation "androidx.compose.material3:material3:1.2.1" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" } // This is uncommented in F-Droid build script diff --git a/mobile/android/app/proguard-rules.pro b/mobile/android/app/proguard-rules.pro index ea6dd795b5..898caee06c 100644 --- a/mobile/android/app/proguard-rules.pro +++ b/mobile/android/app/proguard-rules.pro @@ -25,8 +25,15 @@ @com.google.gson.annotations.SerializedName ; } +# TypeToken preventions +-keep class com.google.gson.reflect.TypeToken { *; } +-keep class * extends com.google.gson.reflect.TypeToken + # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -##---------------End: proguard configuration for Gson ---------- \ No newline at end of file +##---------------End: proguard configuration for Gson ---------- + +# Keep all widget model classes and their fields for Gson +-keep class app.alextran.immich.widget.model.** { *; } \ No newline at end of file diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index cf3b7ee719..3aa72d2876 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -141,6 +141,42 @@ android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> + + + + + + + + + + + + + + + + + + + > + + + + + @@ -154,4 +190,4 @@ - \ No newline at end of file + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt new file mode 100644 index 0000000000..9188df1700 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt @@ -0,0 +1,33 @@ +package app.alextran.immich.widget + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import java.io.File + +fun loadScaledBitmap(file: File, reqWidth: Int, reqHeight: Int): Bitmap? { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeFile(file.absolutePath, options) + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) + options.inJustDecodeBounds = false + + return BitmapFactory.decodeFile(file.absolutePath, options) +} + +fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { + val (height: Int, width: Int) = options.run { outHeight to outWidth } + var inSampleSize = 1 + + if (height > reqHeight || width > reqWidth) { + val halfHeight: Int = height / 2 + val halfWidth: Int = width / 2 + + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2 + } + } + + return inSampleSize +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt new file mode 100644 index 0000000000..2a23d0ecbd --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -0,0 +1,241 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.graphics.Bitmap +import android.util.Log +import androidx.datastore.preferences.core.Preferences +import androidx.glance.* +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.work.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import java.util.UUID +import java.util.concurrent.TimeUnit +import androidx.glance.appwidget.state.getAppWidgetState +import androidx.glance.state.PreferencesGlanceStateDefinition +import app.alextran.immich.widget.model.* +import java.time.LocalDate + +class ImageDownloadWorker( + private val context: Context, + workerParameters: WorkerParameters +) : CoroutineWorker(context, workerParameters) { + + companion object { + + private val uniqueWorkName = ImageDownloadWorker::class.java.simpleName + + private fun buildConstraints(): Constraints { + return Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + } + + private fun buildInputData(appWidgetId: Int, widgetType: WidgetType): Data { + return Data.Builder() + .putString(kWorkerWidgetType, widgetType.toString()) + .putInt(kWorkerWidgetID, appWidgetId) + .build() + } + + fun enqueuePeriodic(context: Context, appWidgetId: Int, widgetType: WidgetType) { + val manager = WorkManager.getInstance(context) + + val workRequest = PeriodicWorkRequestBuilder( + 20, TimeUnit.MINUTES + ) + .setConstraints(buildConstraints()) + .setInputData(buildInputData(appWidgetId, widgetType)) + .addTag(appWidgetId.toString()) + .build() + + manager.enqueueUniquePeriodicWork( + "$uniqueWorkName-$appWidgetId", + ExistingPeriodicWorkPolicy.UPDATE, + workRequest + ) + } + + fun singleShot(context: Context, appWidgetId: Int, widgetType: WidgetType) { + val manager = WorkManager.getInstance(context) + + val workRequest = OneTimeWorkRequestBuilder() + .setConstraints(buildConstraints()) + .setInputData(buildInputData(appWidgetId, widgetType)) + .addTag(appWidgetId.toString()) + .build() + + manager.enqueueUniqueWork( + "$uniqueWorkName-$appWidgetId", + ExistingWorkPolicy.REPLACE, + workRequest + ) + } + + suspend fun cancel(context: Context, appWidgetId: Int) { + WorkManager.getInstance(context).cancelAllWorkByTag("$uniqueWorkName-$appWidgetId") + + // delete cached image + val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(appWidgetId) + val widgetConfig = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentImgUUID = widgetConfig[kImageUUID] + + if (!currentImgUUID.isNullOrEmpty()) { + val file = File(context.cacheDir, imageFilename(currentImgUUID)) + file.delete() + } + } + } + + override suspend fun doWork(): Result { + return try { + val widgetType = WidgetType.valueOf(inputData.getString(kWorkerWidgetType) ?: "") + val widgetId = inputData.getInt(kWorkerWidgetID, -1) + val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(widgetId) + val widgetConfig = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentImgUUID = widgetConfig[kImageUUID] + + val serverConfig = ImmichAPI.getServerConfig(context) + + // clear any image caches and go to "login" state if no credentials + if (serverConfig == null) { + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + updateWidget( + glanceId, + "", + "", + "immich://", + WidgetState.LOG_IN + ) + } + + return Result.success() + } + + // fetch new image + val entry = when (widgetType) { + WidgetType.RANDOM -> fetchRandom(serverConfig, widgetConfig) + WidgetType.MEMORIES -> fetchMemory(serverConfig) + } + + // clear current image if it exists + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + } + + // save a new image + val imgUUID = UUID.randomUUID().toString() + saveImage(entry.image, imgUUID) + + // trigger the update routine with new image uuid + updateWidget(glanceId, imgUUID, entry.subtitle, entry.deeplink) + + Result.success() + } catch (e: Exception) { + Log.e(uniqueWorkName, "Error while loading image", e) + if (runAttemptCount < 10) { + Result.retry() + } else { + Result.failure() + } + } + } + + private suspend fun updateWidget( + glanceId: GlanceId, + imageUUID: String, + subtitle: String?, + deeplink: String?, + widgetState: WidgetState = WidgetState.SUCCESS + ) { + updateAppWidgetState(context, glanceId) { prefs -> + prefs[kNow] = System.currentTimeMillis() + prefs[kImageUUID] = imageUUID + prefs[kWidgetState] = widgetState.toString() + prefs[kSubtitleText] = subtitle ?: "" + prefs[kDeeplinkURL] = deeplink ?: "" + } + + PhotoWidget().update(context,glanceId) + } + + private suspend fun fetchRandom( + serverConfig: ServerConfig, + widgetConfig: Preferences + ): WidgetEntry { + val api = ImmichAPI(serverConfig) + + val filters = SearchFilters(AssetType.IMAGE) + val albumId = widgetConfig[kSelectedAlbum] + val showSubtitle = widgetConfig[kShowAlbumName] + val albumName = widgetConfig[kSelectedAlbumName] + var subtitle: String? = if (showSubtitle == true) albumName else "" + + if (albumId != null) { + filters.albumIds = listOf(albumId) + } + + var randomSearch = api.fetchSearchResults(filters) + + // handle an empty album, fallback to random + if (randomSearch.isEmpty() && albumId != null) { + randomSearch = api.fetchSearchResults(SearchFilters(AssetType.IMAGE)) + subtitle = "" + } + + val random = randomSearch.first() + val image = api.fetchImage(random) + + return WidgetEntry( + image, + subtitle, + assetDeeplink(random) + ) + } + + private suspend fun fetchMemory( + serverConfig: ServerConfig + ): WidgetEntry { + val api = ImmichAPI(serverConfig) + + val today = LocalDate.now() + val memories = api.fetchMemory(today) + val asset: Asset + var subtitle: String? = null + + if (memories.isNotEmpty()) { + // pick a random asset from a random memory + val memory = memories.random() + asset = memory.assets.random() + + val yearDiff = today.year - memory.data.year + subtitle = "$yearDiff ${if (yearDiff == 1) "year" else "years"} ago" + } else { + val filters = SearchFilters(AssetType.IMAGE, size=1) + asset = api.fetchSearchResults(filters).first() + } + + val image = api.fetchImage(asset) + return WidgetEntry( + image, + subtitle, + assetDeeplink(asset) + ) + } + + private suspend fun deleteImage(uuid: String) = withContext(Dispatchers.IO) { + val file = File(context.cacheDir, imageFilename(uuid)) + file.delete() + } + + private suspend fun saveImage(bitmap: Bitmap, uuid: String) = withContext(Dispatchers.IO) { + val file = File(context.cacheDir, imageFilename(uuid)) + FileOutputStream(file).use { out -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out) + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt new file mode 100644 index 0000000000..42f5fb4b1b --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt @@ -0,0 +1,103 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import app.alextran.immich.widget.model.* +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import es.antonborri.home_widget.HomeWidgetPlugin +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.OutputStreamWriter +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +class ImmichAPI(cfg: ServerConfig) { + + companion object { + fun getServerConfig(context: Context): ServerConfig? { + val prefs = HomeWidgetPlugin.getData(context) + + val serverURL = prefs.getString("widget_server_url", "") ?: "" + val sessionKey = prefs.getString("widget_auth_token", "") ?: "" + + if (serverURL.isBlank() || sessionKey.isBlank()) { + return null + } + + return ServerConfig( + serverURL, + sessionKey + ) + } + } + + + private val gson = Gson() + private val serverConfig = cfg + + private fun buildRequestURL(endpoint: String, params: List> = emptyList()): URL { + val urlString = StringBuilder("${serverConfig.serverEndpoint}$endpoint?sessionKey=${serverConfig.sessionKey}") + + for ((key, value) in params) { + urlString.append("&${URLEncoder.encode(key, "UTF-8")}=${URLEncoder.encode(value, "UTF-8")}") + } + + return URL(urlString.toString()) + } + + suspend fun fetchSearchResults(filters: SearchFilters): List = withContext(Dispatchers.IO) { + val url = buildRequestURL("/search/random") + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "POST" + setRequestProperty("Content-Type", "application/json") + doOutput = true + } + + connection.outputStream.use { + OutputStreamWriter(it).use { writer -> + writer.write(gson.toJson(filters)) + writer.flush() + } + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } + + suspend fun fetchMemory(date: LocalDate): List = withContext(Dispatchers.IO) { + val iso8601 = date.format(DateTimeFormatter.ISO_LOCAL_DATE) + val url = buildRequestURL("/memories", listOf("for" to iso8601)) + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } + + suspend fun fetchImage(asset: Asset): Bitmap = withContext(Dispatchers.IO) { + val url = buildRequestURL("/assets/${asset.id}/thumbnail", listOf("size" to "preview")) + val connection = url.openConnection() + val data = connection.getInputStream().readBytes() + BitmapFactory.decodeByteArray(data, 0, data.size) + ?: throw Exception("Invalid image data") + } + + suspend fun fetchAlbums(): List = withContext(Dispatchers.IO) { + val url = buildRequestURL("/albums") + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt new file mode 100644 index 0000000000..7721af7d6f --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt @@ -0,0 +1,56 @@ +package app.alextran.immich.widget + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import androidx.glance.appwidget.GlanceAppWidgetReceiver +import app.alextran.immich.widget.model.* +import es.antonborri.home_widget.HomeWidgetPlugin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class MemoryReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget = PhotoWidget() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + + appWidgetIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + } + + override fun onReceive(context: Context, intent: Intent) { + val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + + // Launch coroutine to setup a single shot if the app requested the update + if (fromMainApp) { + CoroutineScope(Dispatchers.Default).launch { + val provider = ComponentName(context, MemoryReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) + + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) + } + } + } + + super.onReceive(context, intent) + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + CoroutineScope(Dispatchers.Default).launch { + appWidgetIds.forEach { id -> + ImageDownloadWorker.cancel(context, id) + } + } + } +} + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt new file mode 100644 index 0000000000..b1a0a9de31 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt @@ -0,0 +1,124 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.* +import androidx.core.net.toUri +import androidx.datastore.preferences.core.MutablePreferences +import androidx.glance.appwidget.* +import androidx.glance.* +import androidx.glance.action.clickable +import androidx.glance.layout.* +import androidx.glance.state.GlanceStateDefinition +import androidx.glance.state.PreferencesGlanceStateDefinition +import androidx.glance.text.Text +import androidx.glance.text.TextAlign +import androidx.glance.text.TextStyle +import androidx.glance.unit.ColorProvider +import app.alextran.immich.R +import app.alextran.immich.widget.model.* +import java.io.File + +class PhotoWidget : GlanceAppWidget() { + override var stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + val prefs = currentState() + + val imageUUID = prefs[kImageUUID] + val subtitle = prefs[kSubtitleText] + val deeplinkURL = prefs[kDeeplinkURL]?.toUri() + val widgetState = prefs[kWidgetState] + var bitmap: Bitmap? = null + + if (imageUUID != null) { + // fetch a random photo from server + val file = File(context.cacheDir, imageFilename(imageUUID)) + + if (file.exists()) { + bitmap = loadScaledBitmap(file, 500, 500) + } + } + + // WIDGET CONTENT + Box( + modifier = GlanceModifier + .fillMaxSize() + .background(GlanceTheme.colors.background) + .clickable { + val intent = Intent(Intent.ACTION_VIEW, deeplinkURL ?: "immich://".toUri()) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + } + ) { + if (bitmap != null) { + Image( + provider = ImageProvider(bitmap), + contentDescription = "Widget Image", + contentScale = ContentScale.Crop, + modifier = GlanceModifier.fillMaxSize() + ) + + if (!subtitle.isNullOrBlank()) { + Column( + verticalAlignment = Alignment.Bottom, + horizontalAlignment = Alignment.Start, + modifier = GlanceModifier + .fillMaxSize() + .padding(12.dp) + ) { + Text( + text = subtitle, + style = TextStyle( + color = ColorProvider(Color.White), + fontSize = 16.sp + ), + modifier = GlanceModifier + .background(ColorProvider(Color(0x99000000))) // 60% black + .padding(8.dp) + .cornerRadius(8.dp) + ) + } + } + } else { + Column( + modifier = GlanceModifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + provider = ImageProvider(R.drawable.splash), + contentDescription = null, + ) + + if (widgetState == WidgetState.LOG_IN.toString()) { + Box( + modifier = GlanceModifier.fillMaxWidth().padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text("Log in to your Immich server", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary)) + } + } else { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = GlanceModifier.fillMaxWidth().padding(16.dp) + ) { + CircularProgressIndicator( + modifier = GlanceModifier.size(12.dp) + ) + + Spacer(modifier = GlanceModifier.width(8.dp)) + + Text("Loading widget...", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary)) + } + } + } + } + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt new file mode 100644 index 0000000000..39afd76c35 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -0,0 +1,55 @@ +package app.alextran.immich.widget + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import es.antonborri.home_widget.HomeWidgetPlugin +import androidx.glance.appwidget.GlanceAppWidgetReceiver +import app.alextran.immich.widget.model.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class RandomReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget = PhotoWidget() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + + appWidgetIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + } + + override fun onReceive(context: Context, intent: Intent) { + val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + + // Launch coroutine to setup a single shot if the app requested the update + if (fromMainApp) { + CoroutineScope(Dispatchers.Default).launch { + val provider = ComponentName(context, RandomReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) + + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) + } + } + } + + super.onReceive(context, intent) + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + CoroutineScope(Dispatchers.Default).launch { + appWidgetIds.forEach { id -> + ImageDownloadWorker.cancel(context, id) + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt new file mode 100644 index 0000000000..74686ee0b8 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt @@ -0,0 +1,64 @@ +package app.alextran.immich.widget.configure + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.* + + +data class DropdownItem ( + val label: String, + val id: String, +) + +// Creating a composable to display a drop down menu +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Dropdown(items: List, + selectedItem: DropdownItem?, + onItemSelected: (DropdownItem) -> Unit, + enabled: Boolean = true +) { + + var expanded by remember { mutableStateOf(false) } + var selectedOption by remember { mutableStateOf(selectedItem?.label ?: items[0].label) } + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded && enabled }, + ) { + + TextField( + value = selectedOption, + onValueChange = {}, + readOnly = true, + enabled = enabled, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + modifier = Modifier + .fillMaxWidth() + .menuAnchor() + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + items.forEach { option -> + DropdownMenuItem( + text = { Text(option.label, color = MaterialTheme.colorScheme.onSurface) }, + onClick = { + selectedOption = option.label + onItemSelected(option) + + expanded = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding + ) + } + } + } + } + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt new file mode 100644 index 0000000000..efdcc41540 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt @@ -0,0 +1,28 @@ +package app.alextran.immich.widget.configure + +import android.os.Build +import androidx.compose.foundation.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +fun LightDarkTheme( + content: @Composable () -> Unit +) { + val context = LocalContext.current + val isDarkTheme = isSystemInDarkTheme() + + val colorScheme = when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && isDarkTheme -> + dynamicDarkColorScheme(context) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isDarkTheme -> + dynamicLightColorScheme(context) + isDarkTheme -> darkColorScheme() + else -> lightColorScheme() + } + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt new file mode 100644 index 0000000000..d0490c023e --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt @@ -0,0 +1,210 @@ +package app.alextran.immich.widget.configure + +import android.appwidget.AppWidgetManager +import android.content.Context +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceId +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.state.getAppWidgetState +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.glance.state.PreferencesGlanceStateDefinition +import app.alextran.immich.widget.ImageDownloadWorker +import app.alextran.immich.widget.ImmichAPI +import app.alextran.immich.widget.model.* +import kotlinx.coroutines.launch +import java.io.FileNotFoundException + +class RandomConfigure : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Get widget ID from intent + val appWidgetId = intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID) + ?: AppWidgetManager.INVALID_APPWIDGET_ID + + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + return + } + + val glanceId = GlanceAppWidgetManager(applicationContext) + .getGlanceIdBy(appWidgetId) + + setContent { + LightDarkTheme { + RandomConfiguration(applicationContext, appWidgetId, glanceId, onDone = { + finish() + Log.w("WIDGET_ACTIVITY", "SAVING") + }) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RandomConfiguration(context: Context, appWidgetId: Int, glanceId: GlanceId, onDone: () -> Unit) { + + var selectedAlbum by remember { mutableStateOf(null) } + var showAlbumName by remember { mutableStateOf(false) } + var availableAlbums by remember { mutableStateOf>(listOf()) } + var state by remember { mutableStateOf(WidgetConfigState.LOADING) } + + val scope = rememberCoroutineScope() + + LaunchedEffect(Unit) { + // get albums from server + val serverCfg = ImmichAPI.getServerConfig(context) + + if (serverCfg == null) { + state = WidgetConfigState.LOG_IN + return@LaunchedEffect + } + + val api = ImmichAPI(serverCfg) + + val currentState = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentAlbumId = currentState[kSelectedAlbum] ?: "NONE" + val currentAlbumName = currentState[kSelectedAlbumName] ?: "None" + var albumItems: List + + try { + albumItems = api.fetchAlbums().map { + DropdownItem(it.albumName, it.id) + } + + state = WidgetConfigState.SUCCESS + } catch (e: FileNotFoundException) { + Log.e("WidgetWorker", "Error fetching albums: ${e.message}") + + state = WidgetConfigState.NO_CONNECTION + albumItems = listOf(DropdownItem(currentAlbumName, currentAlbumId)) + } + + availableAlbums = listOf(DropdownItem("None", "NONE")) + albumItems + + // load selected configuration + val albumEntity = availableAlbums.firstOrNull { it.id == currentAlbumId } + selectedAlbum = albumEntity ?: availableAlbums.first() + + // load showAlbumName + showAlbumName = currentState[kShowAlbumName] == true + } + + suspend fun saveConfiguration() { + updateAppWidgetState(context, glanceId) { prefs -> + prefs[kSelectedAlbum] = selectedAlbum?.id ?: "" + prefs[kSelectedAlbumName] = selectedAlbum?.label ?: "" + prefs[kShowAlbumName] = showAlbumName + } + + ImageDownloadWorker.singleShot(context, appWidgetId, WidgetType.RANDOM) + } + + Scaffold( + topBar = { + TopAppBar ( + title = { Text("Widget Configuration") }, + actions = { + IconButton(onClick = { + scope.launch { + saveConfiguration() + onDone() + } + }) { + Icon(Icons.Default.Check, contentDescription = "Close", tint = MaterialTheme.colorScheme.primary) + } + } + ) + } + ) { innerPadding -> + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), // Respect the top bar + color = MaterialTheme.colorScheme.background + ) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { + when (state) { + WidgetConfigState.LOADING -> CircularProgressIndicator(modifier = Modifier.size(48.dp)) + WidgetConfigState.LOG_IN -> Text("You must log in inside the Immich App to configure this widget.") + else -> { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text("View a random image from your library or a specific album.", style = MaterialTheme.typography.bodyMedium) + + // no connection warning + if (state == WidgetConfigState.NO_CONNECTION) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.errorContainer) + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Warning, + contentDescription = "Warning", + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "No connection to the server is available. Please try again later.", + style = MaterialTheme.typography.bodyMedium + ) + } + } + + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text("Album") + Dropdown( + items = availableAlbums, + selectedItem = selectedAlbum, + onItemSelected = { selectedAlbum = it }, + enabled = (state != WidgetConfigState.NO_CONNECTION) + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "Show Album Name") + Switch( + checked = showAlbumName, + onCheckedChange = { showAlbumName = it }, + enabled = (state != WidgetConfigState.NO_CONNECTION) + ) + } + } + } + } + } + } + } + } +} + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt new file mode 100644 index 0000000000..2337de0612 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt @@ -0,0 +1,79 @@ +package app.alextran.immich.widget.model + +import android.graphics.Bitmap +import androidx.datastore.preferences.core.* + +// MARK: Immich Entities + +enum class AssetType { + IMAGE, VIDEO, AUDIO, OTHER +} + +data class Asset( + val id: String, + val type: AssetType, +) + +data class SearchFilters( + var type: AssetType = AssetType.IMAGE, + val size: Int = 1, + var albumIds: List = listOf() +) + +data class MemoryResult( + val id: String, + var assets: List, + val type: String, + val data: MemoryData +) { + data class MemoryData(val year: Int) +} + +data class Album( + val id: String, + val albumName: String +) + +// MARK: Widget Specific + +enum class WidgetType { + RANDOM, MEMORIES; +} + +enum class WidgetState { + LOADING, SUCCESS, LOG_IN; +} + +enum class WidgetConfigState { + LOADING, SUCCESS, LOG_IN, NO_CONNECTION +} + +data class WidgetEntry ( + val image: Bitmap, + val subtitle: String?, + val deeplink: String? +) + +data class ServerConfig(val serverEndpoint: String, val sessionKey: String) + +// MARK: Widget State Keys +val kImageUUID = stringPreferencesKey("uuid") +val kSubtitleText = stringPreferencesKey("subtitle") +val kNow = longPreferencesKey("now") +val kWidgetState = stringPreferencesKey("state") +val kSelectedAlbum = stringPreferencesKey("albumID") +val kSelectedAlbumName = stringPreferencesKey("albumName") +val kShowAlbumName = booleanPreferencesKey("showAlbumName") +val kDeeplinkURL = stringPreferencesKey("deeplink") + +const val kWorkerWidgetType = "widgetType" +const val kWorkerWidgetID = "widgetId" +const val kTriggeredFromApp = "triggeredFromApp" + +fun imageFilename(id: String): String { + return "widget_image_$id.jpg" +} + +fun assetDeeplink(asset: Asset): String { + return "immich://asset?id=${asset.id}" +} diff --git a/mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png b/mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..97aceb3ef6a1f91071af9c4f444f0f77254bf255 GIT binary patch literal 246192 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 z֯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

1BQ}t6PiNT8@GtCPq!?H%sm#N~P)qnHPx-Kls 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�sH+P##kT_@3CnrC?zor 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�x*9JHH`C2_v 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 DQTQ90 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-nodpi/random_preview.png b/mobile/android/app/src/main/res/drawable-nodpi/random_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..f94d1bbcd55126c21c2744745171898561d037af GIT binary patch literal 250040 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ⅆJh%lN#KuzO{b@n}ex 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 pEbinsdr@Ef_WPgrX8RKu{~u}<@R + + Memories + Random + + See memories from Immich. + View a random image from your library or a specific album. + diff --git a/mobile/android/app/src/main/res/xml/memory_widget.xml b/mobile/android/app/src/main/res/xml/memory_widget.xml new file mode 100644 index 0000000000..611c5aae02 --- /dev/null +++ b/mobile/android/app/src/main/res/xml/memory_widget.xml @@ -0,0 +1,9 @@ + diff --git a/mobile/android/app/src/main/res/xml/random_widget.xml b/mobile/android/app/src/main/res/xml/random_widget.xml new file mode 100644 index 0000000000..25fb24754f --- /dev/null +++ b/mobile/android/app/src/main/res/xml/random_widget.xml @@ -0,0 +1,13 @@ + diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 6d98152efc..c37498ea3e 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -28,7 +28,8 @@ const String appShareGroupId = "group.app.immich.share"; // add widget identifiers here for new widgets // these are used to force a widget refresh -const List kWidgetNames = [ - 'com.immich.widget.random', - 'com.immich.widget.memory', +// (iOSName, androidFQDN) +const List<(String, String)> kWidgetNames = [ + ('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiver'), + ('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiver'), ]; diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart index be314a281e..09532f4b78 100644 --- a/mobile/lib/repositories/widget.repository.dart +++ b/mobile/lib/repositories/widget.repository.dart @@ -10,8 +10,11 @@ class WidgetRepository { await HomeWidget.saveWidgetData(key, value); } - Future refresh(String name) async { - await HomeWidget.updateWidget(name: name, iOSName: name); + Future refresh(String iosName, String androidName) async { + await HomeWidget.updateWidget( + iOSName: iosName, + qualifiedAndroidName: androidName, + ); } Future setAppGroupId(String appGroupId) async { diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index 02ddedbe89..fb2022784f 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/repositories/widget.repository.dart'; @@ -33,10 +32,8 @@ class WidgetService { } Future refreshWidgets() async { - if (Platform.isAndroid) return; - - for (final name in kWidgetNames) { - await _repository.refresh(name); + for (final (iOSName, androidName) in kWidgetNames) { + await _repository.refresh(iOSName, androidName); } } } From 493d85b02103f371c83f9cc2aac93e2bdc46b7f1 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 18 Jul 2025 10:57:29 -0400 Subject: [PATCH 005/169] feat!: absolute file paths (#19995) feat: absolute file paths --- docs/docs/administration/server-commands.md | 42 ++- docs/docs/install/environment-variables.md | 28 +- server/src/app.module.ts | 4 +- server/src/commands/index.ts | 10 +- server/src/commands/media-location.command.ts | 106 +++++++ server/src/constants.ts | 2 +- server/src/dtos/env.dto.ts | 6 +- server/src/enum.ts | 2 + server/src/queries/asset.repository.sql | 24 ++ server/src/queries/person.repository.sql | 11 + server/src/queries/user.repository.sql | 11 + server/src/repositories/asset.repository.ts | 18 ++ .../repositories/config.repository.spec.ts | 12 +- server/src/repositories/config.repository.ts | 8 +- .../src/repositories/database.repository.ts | 33 +++ server/src/repositories/person.repository.ts | 10 + server/src/repositories/user.repository.ts | 10 + .../1752759108283-ConvertToAbsolutePaths.ts | 39 +++ .../src/services/asset-media.service.spec.ts | 10 +- server/src/services/auth.service.spec.ts | 2 +- server/src/services/cli.service.ts | 58 ++++ server/src/services/download.service.spec.ts | 15 +- server/src/services/library.service.spec.ts | 4 +- server/src/services/media.service.spec.ts | 273 +++++++++--------- server/src/services/metadata.service.spec.ts | 6 +- server/src/services/server.service.spec.ts | 12 +- .../services/storage-template.service.spec.ts | 72 +++-- server/src/services/storage.service.spec.ts | 58 ++-- server/src/services/storage.service.ts | 19 +- server/src/services/user.service.spec.ts | 27 +- server/src/types.ts | 2 + server/src/utils/file.ts | 10 +- .../repositories/asset.repository.mock.ts | 1 + .../repositories/database.repository.mock.ts | 1 + 34 files changed, 689 insertions(+), 257 deletions(-) create mode 100644 server/src/commands/media-location.command.ts create mode 100644 server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index b414f5deaa..b275d8fede 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -2,16 +2,17 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands: -| Command | Description | -| ------------------------ | ------------------------------------- | -| `help` | Display help | -| `reset-admin-password` | Reset the password for the admin user | -| `disable-password-login` | Disable password login | -| `enable-password-login` | Enable password login | -| `enable-oauth-login` | Enable OAuth login | -| `disable-oauth-login` | Disable OAuth login | -| `list-users` | List Immich users | -| `version` | Print Immich version | +| Command | Description | +| ------------------------ | ------------------------------------------------------------- | +| `help` | Display help | +| `reset-admin-password` | Reset the password for the admin user | +| `disable-password-login` | Disable password login | +| `enable-password-login` | Enable password login | +| `enable-oauth-login` | Enable OAuth login | +| `disable-oauth-login` | Disable OAuth login | +| `list-users` | List Immich users | +| `version` | Print Immich version | +| `change-media-location` | Change database file paths to align with a new media location | ## How to run a command @@ -88,3 +89,24 @@ Print Immich Version immich-admin version v1.129.0 ``` + +Change media location + +``` +immich-admin change-media-location +? Enter the previous value of IMMICH_MEDIA_LOCATION: /usr/src/app/upload +? Enter the new value of IMMICH_MEDIA_LOCATION: /data + + Previous value: /usr/src/app/upload + Current value: /data + + Changing database paths from "/usr/src/app/upload/*" to "/data/*" + +? Do you want to proceed? [Y/n] y + +Database file paths updated successfully! 🎉 + +You may now set IMMICH_MEDIA_LOCATION=/data and restart! + +(please remember to update applicable volume mounts e.g. ${UPLOAD_LOCATION}:/data) +``` diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index b9dab5b9c8..8d5ab55049 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -29,20 +29,20 @@ These environment variables are used by the `docker-compose.yml` file and do **N ## General -| Variable | Description | Default | Containers | Workers | -| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- | -| `TZ` | Timezone | \*1 | server | microservices | -| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | -| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `./upload`\*3 | server | api, microservices | -| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | -| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | -| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | -| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | -| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | -| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | -| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | -| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | +| Variable | Description | Default | Containers | Workers | +| :---------------------------------- | :---------------------------------------------------------------------------------------- | :---------------------------------: | :----------------------- | :----------------- | +| `TZ` | Timezone | \*1 | server | microservices | +| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | +| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload`\*3 | server | api, microservices | +| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | +| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | +| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | +| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | +| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | +| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | +| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | +| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | \*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution. diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 9ea75d78c4..8d261463e7 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -5,7 +5,7 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { ClsModule } from 'nestjs-cls'; import { KyselyModule } from 'nestjs-kysely'; import { OpenTelemetryModule } from 'nestjs-otel'; -import { commands } from 'src/commands'; +import { commandsAndQuestions } from 'src/commands'; import { IWorker } from 'src/constants'; import { controllers } from 'src/controllers'; import { ImmichWorker } from 'src/enum'; @@ -97,7 +97,7 @@ export class MicroservicesModule extends BaseModule {} @Module({ imports: [...imports], - providers: [...common, ...commands, SchedulerRegistry], + providers: [...common, ...commandsAndQuestions, SchedulerRegistry], }) export class ImmichAdminModule implements OnModuleDestroy { constructor(private service: CliService) {} diff --git a/server/src/commands/index.ts b/server/src/commands/index.ts index ce085f6e34..46a8d13e35 100644 --- a/server/src/commands/index.ts +++ b/server/src/commands/index.ts @@ -1,11 +1,16 @@ import { GrantAdminCommand, PromptEmailQuestion, RevokeAdminCommand } from 'src/commands/grant-admin'; import { ListUsersCommand } from 'src/commands/list-users.command'; +import { + ChangeMediaLocationCommand, + PromptConfirmMoveQuestions, + PromptMediaLocationQuestions, +} from 'src/commands/media-location.command'; import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login'; import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login'; import { PromptPasswordQuestions, ResetAdminPasswordCommand } from 'src/commands/reset-admin-password.command'; import { VersionCommand } from 'src/commands/version.command'; -export const commands = [ +export const commandsAndQuestions = [ ResetAdminPasswordCommand, PromptPasswordQuestions, PromptEmailQuestion, @@ -17,4 +22,7 @@ export const commands = [ VersionCommand, GrantAdminCommand, RevokeAdminCommand, + ChangeMediaLocationCommand, + PromptMediaLocationQuestions, + PromptConfirmMoveQuestions, ]; diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts new file mode 100644 index 0000000000..0935fe202d --- /dev/null +++ b/server/src/commands/media-location.command.ts @@ -0,0 +1,106 @@ +import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander'; +import { CliService } from 'src/services/cli.service'; + +@Command({ + name: 'change-media-location', + description: 'Change database file paths to align with a new media location', +}) +export class ChangeMediaLocationCommand extends CommandRunner { + constructor( + private service: CliService, + private inquirer: InquirerService, + ) { + super(); + } + + private async showSamplePaths(hint?: string) { + hint = hint ? ` (${hint})` : ''; + + const paths = await this.service.getSampleFilePaths(); + if (paths.length > 0) { + let message = ` Examples from the database${hint}:\n`; + for (const path of paths) { + message += ` - ${path}\n`; + } + + console.log(`\n${message}`); + } + } + + async run(): Promise { + try { + await this.showSamplePaths(); + + const { oldValue, newValue } = await this.inquirer.ask<{ oldValue: string; newValue: string }>( + 'prompt-media-location', + {}, + ); + + const success = await this.service.migrateFilePaths({ + oldValue, + newValue, + confirm: async ({ sourceFolder, targetFolder }) => { + console.log(` + Previous value: ${oldValue} + Current value: ${newValue} + + Changing from "${sourceFolder}/*" to "${targetFolder}/*" +`); + + const { value: confirmed } = await this.inquirer.ask<{ value: boolean }>('prompt-confirm-move', {}); + return confirmed; + }, + }); + + const successMessage = `Matching database file paths were updated successfully! 🎉 + + You may now set IMMICH_MEDIA_LOCATION=${newValue} and restart! + + (please remember to update applicable volume mounts e.g + services: + immich-server: + ... + volumes: + - \${UPLOAD_LOCATION}:/usr/src/app/upload + ... + )`; + + console.log(`\n ${success ? successMessage : 'No rows were updated'}\n`); + + await this.showSamplePaths('after'); + } catch (error) { + console.error(error); + console.error('Unable to update database file paths.'); + } + } +} + +const currentValue = process.env.IMMICH_MEDIA_LOCATION || ''; + +const makePrompt = (which: string) => { + return `Enter the ${which} value of IMMICH_MEDIA_LOCATION:${currentValue ? ` [${currentValue}]` : ''}`; +}; + +@QuestionSet({ name: 'prompt-media-location' }) +export class PromptMediaLocationQuestions { + @Question({ message: makePrompt('previous'), name: 'oldValue' }) + oldValue(value: string) { + return value || currentValue; + } + + @Question({ message: makePrompt('new'), name: 'newValue' }) + newValue(value: string) { + return value || currentValue; + } +} + +@QuestionSet({ name: 'prompt-confirm-move' }) +export class PromptConfirmMoveQuestions { + @Question({ + message: 'Do you want to proceed? [Y/n]', + name: 'value', + }) + value(value: string): boolean { + return ['yes', 'y'].includes((value || 'y').toLowerCase()); + } +} diff --git a/server/src/constants.ts b/server/src/constants.ts index 447d8a09c9..2d803c2e95 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -47,7 +47,7 @@ export const serverVersion = new SemVer(version); export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; +export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || '/usr/src/app/upload'; export const MACHINE_LEARNING_PING_TIMEOUT = Number(process.env.MACHINE_LEARNING_PING_TIMEOUT || 2000); export const MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME = Number( diff --git a/server/src/dtos/env.dto.ts b/server/src/dtos/env.dto.ts index 99fd1d2149..3543d8dae9 100644 --- a/server/src/dtos/env.dto.ts +++ b/server/src/dtos/env.dto.ts @@ -1,5 +1,5 @@ import { Transform, Type } from 'class-transformer'; -import { IsEnum, IsInt, IsString } from 'class-validator'; +import { IsEnum, IsInt, IsString, Matches } from 'class-validator'; import { DatabaseSslMode, ImmichEnvironment, LogLevel } from 'src/enum'; import { IsIPRange, Optional, ValidateBoolean } from 'src/validation'; @@ -48,6 +48,10 @@ export class EnvDto { @Optional() IMMICH_LOG_LEVEL?: LogLevel; + @Optional() + @Matches(/^\//, { message: 'IMMICH_MEDIA_LOCATION must be an absolute path' }) + IMMICH_MEDIA_LOCATION?: string; + @IsInt() @Optional() @Type(() => Number) diff --git a/server/src/enum.ts b/server/src/enum.ts index 587a76126f..e41a790999 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -198,6 +198,7 @@ export enum StorageFolder { } export enum SystemMetadataKey { + MediaLocation = 'MediaLocation', ReverseGeocodingState = 'reverse-geocoding-state', FacialRecognitionState = 'facial-recognition-state', MemoriesState = 'memories-state', @@ -544,6 +545,7 @@ export enum DatabaseLock { CLIPDimSize = 512, Library = 1337, NightlyJobs = 600, + MediaLocation = 700, GetSystemConfig = 69, BackupDatabase = 42, MemoryCreation = 777, diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index e482a38a9a..9425bd9a11 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -168,6 +168,30 @@ from where "livePhotoVideoId" = $1::uuid +-- AssetRepository.getFileSamples +select + "asset"."id", + "asset"."originalPath", + "asset"."sidecarPath", + "asset"."encodedVideoPath", + ( + select + coalesce(json_agg(agg), '[]') + from + ( + select + "path" + from + "asset_file" + where + "asset"."id" = "asset_file"."assetId" + ) as agg + ) as "files" +from + "asset" +limit + 3 + -- AssetRepository.getById select "asset".* diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 3e41edde9c..8ad5b96bbc 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -12,6 +12,17 @@ delete from "person" where "person"."id" in ($1) +-- PersonRepository.getFileSamples +select + "id", + "thumbnailPath" +from + "person" +where + "thumbnailPath" != '' +limit + 3 + -- PersonRepository.getAllForUser select "person".* diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index f1809464bf..6a02654781 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -78,6 +78,17 @@ where "user"."isAdmin" = $1 and "user"."deletedAt" is null +-- UserRepository.getFileSamples +select + "id", + "profileImagePath" +from + "user" +where + "profileImagePath" != '' +limit + 3 + -- UserRepository.hasAdmin select "user"."id" diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index cb7e804f6f..edbafaa22d 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; +import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; @@ -335,6 +336,23 @@ export class AssetRepository { return count; } + @GenerateSql() + getFileSamples() { + return this.db + .selectFrom('asset') + .select((eb) => [ + 'asset.id', + 'asset.originalPath', + 'asset.sidecarPath', + 'asset.encodedVideoPath', + jsonArrayFrom(eb.selectFrom('asset_file').select('path').whereRef('asset.id', '=', 'asset_file.assetId')).as( + 'files', + ), + ]) + .limit(sql.lit(3)) + .execute(); + } + @GenerateSql({ params: [DummyValue.UUID] }) getById(id: string, { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}) { return this.db diff --git a/server/src/repositories/config.repository.spec.ts b/server/src/repositories/config.repository.spec.ts index a096c6a4bc..99cba43b99 100644 --- a/server/src/repositories/config.repository.spec.ts +++ b/server/src/repositories/config.repository.spec.ts @@ -13,6 +13,7 @@ const resetEnv = () => { 'IMMICH_WORKERS_EXCLUDE', 'IMMICH_TRUSTED_PROXIES', 'IMMICH_API_METRICS_PORT', + 'IMMICH_MEDIA_LOCATION', 'IMMICH_MICROSERVICES_METRICS_PORT', 'IMMICH_TELEMETRY_INCLUDE', 'IMMICH_TELEMETRY_EXCLUDE', @@ -76,6 +77,13 @@ describe('getEnv', () => { }); }); + describe('IMMICH_MEDIA_LOCATION', () => { + it('should throw an error for relative paths', () => { + process.env.IMMICH_MEDIA_LOCATION = './relative/path'; + expect(() => getEnv()).toThrowError('IMMICH_MEDIA_LOCATION must be an absolute path'); + }); + }); + describe('database', () => { it('should use defaults', () => { const { database } = getEnv(); @@ -95,7 +103,7 @@ describe('getEnv', () => { it('should validate DB_SSL_MODE', () => { process.env.DB_SSL_MODE = 'invalid'; - expect(() => getEnv()).toThrowError('Invalid environment variables: DB_SSL_MODE'); + expect(() => getEnv()).toThrowError('DB_SSL_MODE must be one of the following values:'); }); it('should accept a valid DB_SSL_MODE', () => { @@ -239,7 +247,7 @@ describe('getEnv', () => { it('should reject invalid trusted proxies', () => { process.env.IMMICH_TRUSTED_PROXIES = '10.1'; - expect(() => getEnv()).toThrowError('Invalid environment variables: IMMICH_TRUSTED_PROXIES'); + expect(() => getEnv()).toThrow('IMMICH_TRUSTED_PROXIES must be an ip address, or ip address range'); }); }); diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index 7038338927..c9e96a1803 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -131,9 +131,11 @@ const getEnv = (): EnvData => { const dto = plainToInstance(EnvDto, process.env); const errors = validateSync(dto); if (errors.length > 0) { - throw new Error( - `Invalid environment variables: ${errors.map((error) => `${error.property}=${error.value}`).join(', ')}`, - ); + const messages = [`Invalid environment variables: `]; + for (const error of errors) { + messages.push(` - ${error.property}=${error.value} (${Object.values(error.constraints || {}).join(', ')})`); + } + throw new Error(messages.join('\n')); } const includedWorkers = asSet(dto.IMMICH_WORKERS_INCLUDE, [ImmichWorker.Api, ImmichWorker.Microservices]); diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 8b5c728ce4..1f83630cfa 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -436,6 +436,39 @@ export class DatabaseRepository { this.logger.debug('Finished running kysely migrations'); } + async migrateFilePaths(sourceFolder: string, targetFolder: string): Promise { + // escaping regex special characters with a backslash + const sourceRegex = '^' + sourceFolder.replaceAll(/[-[\]{}()*+?.,\\^$|#\s]/g, String.raw`\$&`); + const source = sql.raw(`'${sourceRegex}'`); + const target = sql.lit(targetFolder); + + await this.db.transaction().execute(async (tx) => { + await tx + .updateTable('asset') + .set((eb) => ({ + originalPath: eb.fn('REGEXP_REPLACE', ['originalPath', source, target]), + encodedVideoPath: eb.fn('REGEXP_REPLACE', ['encodedVideoPath', source, target]), + sidecarPath: eb.fn('REGEXP_REPLACE', ['sidecarPath', source, target]), + })) + .execute(); + + await tx + .updateTable('asset_file') + .set((eb) => ({ path: eb.fn('REGEXP_REPLACE', ['path', source, target]) })) + .execute(); + + await tx + .updateTable('person') + .set((eb) => ({ thumbnailPath: eb.fn('REGEXP_REPLACE', ['thumbnailPath', source, target]) })) + .execute(); + + await tx + .updateTable('user') + .set((eb) => ({ profileImagePath: eb.fn('REGEXP_REPLACE', ['profileImagePath', source, target]) })) + .execute(); + }); + } + async withLock(lock: DatabaseLock, callback: () => Promise): Promise { let res; await this.asyncLock.acquire(DatabaseLock[lock], async () => { diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 5b7d1d3700..f653bb8179 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -142,6 +142,16 @@ export class PersonRepository { .stream(); } + @GenerateSql() + getFileSamples() { + return this.db + .selectFrom('person') + .select(['id', 'thumbnailPath']) + .where('thumbnailPath', '!=', sql.lit('')) + .limit(sql.lit(3)) + .execute(); + } + @GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] }) async getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions) { const items = await this.db diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index 715aa2dc32..9d5f19b26a 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -79,6 +79,16 @@ export class UserRepository { .executeTakeFirst(); } + @GenerateSql() + getFileSamples() { + return this.db + .selectFrom('user') + .select(['id', 'profileImagePath']) + .where('profileImagePath', '!=', sql.lit('')) + .limit(sql.lit(3)) + .execute(); + } + @GenerateSql() async hasAdmin(): Promise { const admin = await this.db diff --git a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts new file mode 100644 index 0000000000..68b0c7931e --- /dev/null +++ b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts @@ -0,0 +1,39 @@ +import { Kysely, sql } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; + +const logger = LoggingRepository.create(); +logger.setContext('Migrations'); + +export async function up(db: Kysely): Promise { + if (process.env.IMMICH_MEDIA_LOCATION) { + // do not automatically convert paths for a custom location/setting + return; + } + + // we construct paths using `path.join(mediaLocation, ...)`, which strips the leading './' + const source = 'upload'; + const target = '/usr/src/app/upload'; + + logger.log(`Converting database file paths from relative to absolute (source=${source}/*, target=${target}/*)`); + + // escaping regex special characters with a backslash + const sourceRegex = '^' + source.replaceAll(/[-[\]{}()*+?.,\\^$|#\s]/g, String.raw`\$&`); + + const items: Array<{ table: string; column: string }> = [ + { table: 'asset', column: 'originalPath' }, + { table: 'asset', column: 'encodedVideoPath' }, + { table: 'asset', column: 'sidecarPath' }, + { table: 'asset_file', column: 'path' }, + { table: 'person', column: 'thumbnailPath' }, + { table: 'user', column: 'profileImagePath' }, + ]; + + for (const { table, column } of items) { + const query = `UPDATE "${table}" SET "${column}" = REGEXP_REPLACE("${column}", '${sourceRegex}', '${target}') WHERE "${column}" IS NOT NULL`; + await sql.raw(query).execute(db); + } +} + +export async function down(): Promise { + // not supported +} diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index dccb79c585..08b42b6cbf 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -294,16 +294,16 @@ describe(AssetMediaService.name, () => { it('should return profile for profile uploads', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.PROFILE_DATA, 'image.jpg'))).toEqual( - 'upload/profile/admin_id', + expect.stringContaining('upload/profile/admin_id'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/profile/admin_id'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile/admin_id')); }); it('should return upload for everything else', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.ASSET_DATA, 'image.jpg'))).toEqual( - 'upload/upload/admin_id/ra/nd', + expect.stringContaining('upload/upload/admin_id/ra/nd'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/upload/admin_id/ra/nd'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload/admin_id/ra/nd')); }); }); @@ -913,7 +913,7 @@ describe(AssetMediaService.name, () => { expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FileDelete, - data: { files: ['upload/upload/user-id/ra/nd/random-uuid.jpg'] }, + data: { files: [expect.stringContaining('upload/upload/user-id/ra/nd/random-uuid.jpg')] }, }); }); }); diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index f52fd9dd81..129877bbdd 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -789,7 +789,7 @@ describe(AuthService.name, () => { ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { - profileImagePath: `upload/profile/${user.id}/${fileId}.jpg`, + profileImagePath: expect.stringContaining(`upload/profile/${user.id}/${fileId}.jpg`), profileChangedAt: expect.any(Date), }); expect(mocks.oauth.getProfilePicture).toHaveBeenCalledWith(pictureUrl); diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 021a5240f6..674b885dc4 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { isAbsolute } from 'node:path'; import { SALT_ROUNDS } from 'src/constants'; import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto'; import { BaseService } from 'src/services/base.service'; @@ -67,6 +68,63 @@ export class CliService extends BaseService { await this.updateConfig(config); } + async getSampleFilePaths(): Promise { + const [assets, people, users] = await Promise.all([ + this.assetRepository.getFileSamples(), + this.personRepository.getFileSamples(), + this.userRepository.getFileSamples(), + ]); + + const paths = []; + + for (const person of people) { + paths.push(person.thumbnailPath); + } + + for (const user of users) { + paths.push(user.profileImagePath); + } + + for (const asset of assets) { + paths.push( + asset.originalPath, + asset.sidecarPath, + asset.encodedVideoPath, + ...asset.files.map((file) => file.path), + ); + } + + return paths.filter(Boolean) as string[]; + } + + async migrateFilePaths({ + oldValue, + newValue, + confirm, + }: { + oldValue: string; + newValue: string; + confirm: (data: { sourceFolder: string; targetFolder: string }) => Promise; + }): Promise { + let sourceFolder = oldValue; + if (sourceFolder.startsWith('./')) { + sourceFolder = sourceFolder.slice(2); + } + + const targetFolder = newValue; + if (!isAbsolute(targetFolder)) { + throw new Error('Target media location must be an absolute path'); + } + + if (!(await confirm({ sourceFolder, targetFolder }))) { + return false; + } + + await this.databaseRepository.migrateFilePaths(sourceFolder, targetFolder); + + return true; + } + cleanup() { return this.databaseRepository.shutdown(); } diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index 7646637093..940767ff67 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,4 +1,5 @@ import { BadRequestException } from '@nestjs/common'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -46,7 +47,11 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(1); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith( + 1, + expect.stringContaining('upload/library/IMG_123.jpg'), + 'IMG_123.jpg', + ); }); it('should log a warning if the original path could not be resolved', async () => { @@ -279,9 +284,15 @@ describe(DownloadService.name, () => { mocks.downloadRepository.downloadAssetIds.mockReturnValue( makeStream([{ id: 'asset-1', livePhotoVideoId: 'asset-3', size: 5000 }]), ); + mocks.downloadRepository.downloadMotionAssetIds.mockReturnValue( makeStream([ - { id: 'asset-2', livePhotoVideoId: null, size: 23_456, originalPath: 'upload/encoded-video/uuid-MP.mp4' }, + { + id: 'asset-2', + livePhotoVideoId: null, + size: 23_456, + originalPath: APP_MEDIA_LOCATION + '/encoded-video/uuid-MP.mp4', + }, ]), ); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index dac2b64ebc..308c80fb37 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; +import { APP_MEDIA_LOCATION, JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { LibraryService } from 'src/services/library.service'; @@ -1264,7 +1264,7 @@ describe(LibraryService.name, () => { }); it('should detect when import path is in immich media folder', async () => { - const importPaths = ['upload/thumbs', `${process.cwd()}/xyz`, 'upload/library']; + const importPaths = [APP_MEDIA_LOCATION + '/thumbs', `${process.cwd()}/xyz`, APP_MEDIA_LOCATION + '/library']; const library = factory.library({ importPaths }); mocks.storage.stat.mockResolvedValue({ isDirectory: () => true } as Stats); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index d3c361bb8a..0f4ba769c0 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,5 +1,6 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { Exif } from 'src/database'; import { AssetFileType, @@ -204,19 +205,19 @@ describe(MediaService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.FullSize, oldPath: '/uploads/user-id/fullsize/path.webp', - newPath: 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Preview, oldPath: '/uploads/user-id/thumbs/path.jpg', - newPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-preview.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Thumbnail, oldPath: '/uploads/user-id/webp/path.ext', - newPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-thumbnail.webp'), }); expect(mocks.move.create).toHaveBeenCalledTimes(3); }); @@ -295,7 +296,7 @@ describe(MediaService.name, () => { await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { @@ -315,7 +316,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, @@ -327,7 +328,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + expect.any(String), ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); @@ -341,12 +342,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer }); @@ -357,10 +358,10 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); await sut.handleGenerateThumbnails({ id: assetStub.video.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -377,12 +378,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); }); @@ -392,10 +393,10 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); await sut.handleGenerateThumbnails({ id: assetStub.video.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -412,12 +413,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); }); @@ -432,7 +433,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -453,7 +454,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-sws_flags accurate_rnd+full_chroma_int'], outputOptions: expect.any(Array), @@ -471,7 +472,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringContaining('scale=-2:1440')]), @@ -485,12 +486,12 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = `upload/thumbs/user-id/as/se/asset-id-preview.${format}`; - const thumbnailPath = `upload/thumbs/user-id/as/se/asset-id-thumbnail.webp`; + const previewPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-preview.${format}`; + const thumbnailPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-thumbnail.webp`; await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { colorspace: Colorspace.Srgb, @@ -530,12 +531,12 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = `upload/thumbs/user-id/as/se/asset-id-preview.jpeg`; - const thumbnailPath = `upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`; + const previewPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-preview.jpeg`); + const thumbnailPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { colorspace: Colorspace.Srgb, @@ -658,12 +659,12 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, expect.objectContaining({ processInvalidImages: false }), - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, expect.objectContaining({ processInvalidImages: false }), - 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + expect.any(String), ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); @@ -704,7 +705,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -734,7 +735,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( fullsizeBuffer, @@ -746,7 +747,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -774,7 +775,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, @@ -786,7 +787,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -815,7 +816,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.any(String), ); }); @@ -838,7 +839,7 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).not.toHaveBeenCalledWith( expect.anything(), expect.anything(), - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.stringContaining('fullsize.jpeg'), ); }); @@ -869,7 +870,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + expect.any(String), ); }); }); @@ -911,7 +912,7 @@ describe(MediaService.name, () => { ); expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.originalPath, { colorspace: Colorspace.P3, orientation: undefined, @@ -933,12 +934,9 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); - expect(mocks.person.update).toHaveBeenCalledWith({ - id: 'person-1', - thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - }); + expect(mocks.person.update).toHaveBeenCalledWith({ id: 'person-1', thumbnailPath: expect.any(String) }); }); it('should use preview path if video', async () => { @@ -953,7 +951,7 @@ describe(MediaService.name, () => { ); expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.previewPath, { colorspace: Colorspace.P3, orientation: undefined, @@ -975,12 +973,9 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); - expect(mocks.person.update).toHaveBeenCalledWith({ - id: 'person-1', - thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - }); + expect(mocks.person.update).toHaveBeenCalledWith({ id: 'person-1', thumbnailPath: expect.any(String) }); }); it('should generate a thumbnail without going negative', async () => { @@ -1015,7 +1010,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1052,7 +1047,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1089,7 +1084,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1126,7 +1121,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1168,7 +1163,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1288,7 +1283,7 @@ describe(MediaService.name, () => { expect(mocks.storage.mkdirSync).toHaveBeenCalled(); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-map 0:1', '-map 0:3']), @@ -1308,7 +1303,7 @@ describe(MediaService.name, () => { expect(mocks.storage.mkdirSync).toHaveBeenCalled(); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:2']), @@ -1354,7 +1349,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1369,7 +1364,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1384,7 +1379,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1399,7 +1394,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1416,7 +1411,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('scale')]), @@ -1431,7 +1426,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=-2:720/)]), @@ -1446,7 +1441,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=720:-2/)]), @@ -1463,7 +1458,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=-2:354/)]), @@ -1480,7 +1475,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=354:-2/)]), @@ -1497,7 +1492,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-c:a aac']), @@ -1518,7 +1513,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining(['-tag:v hvc1']), @@ -1539,7 +1534,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-tag:v hvc1']), @@ -1554,7 +1549,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy']), @@ -1568,7 +1563,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-c:a copy']), @@ -1627,7 +1622,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-maxrate 4500k', '-bufsize 9000k']), @@ -1642,7 +1637,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-maxrate 4500k', '-bufsize 9000k']), @@ -1657,7 +1652,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-b:v 3104k', '-minrate 1552k', '-maxrate 4500k']), @@ -1672,7 +1667,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy']), @@ -1693,7 +1688,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-b:v 3104k', '-minrate 1552k', '-maxrate 4500k']), @@ -1714,7 +1709,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-maxrate')]), @@ -1729,7 +1724,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-cpu-used 2']), @@ -1744,7 +1739,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-cpu-used')]), @@ -1759,7 +1754,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-threads 2']), @@ -1774,7 +1769,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-threads 1', '-x264-params frame-threads=1:pools=none']), @@ -1789,7 +1784,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-threads')]), @@ -1804,7 +1799,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v hevc', '-threads 1', '-x265-params frame-threads=1:pools=none']), @@ -1819,7 +1814,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-threads')]), @@ -1834,7 +1829,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -1859,7 +1854,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-preset 4']), @@ -1874,7 +1869,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params mbr=2M']), @@ -1889,7 +1884,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params lp=4']), @@ -1906,7 +1901,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params lp=4:mbr=2M']), @@ -1950,7 +1945,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining([ @@ -1987,7 +1982,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining([expect.stringContaining('-multipass')]), @@ -2004,7 +1999,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining(['-cq:v 23', '-maxrate 10000k', '-bufsize 6897k']), @@ -2021,7 +2016,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.stringContaining('-maxrate'), @@ -2038,7 +2033,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.arrayContaining([expect.stringContaining('-preset')]), @@ -2053,7 +2048,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.arrayContaining([expect.stringContaining('-multipass')]), @@ -2070,7 +2065,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel cuda', @@ -2092,7 +2087,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel cuda', '-hwaccel_output_format cuda']), outputOptions: expect.arrayContaining([ @@ -2113,7 +2108,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel cuda', '-hwaccel_output_format cuda']), outputOptions: expect.arrayContaining([expect.stringContaining('scale_cuda=-2:720:format=nv12')]), @@ -2130,7 +2125,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2170,7 +2165,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2190,7 +2185,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2210,7 +2205,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2239,7 +2234,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD129', @@ -2261,7 +2256,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2287,7 +2282,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2315,7 +2310,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel qsv', '-qsv_device /dev/dri/renderD129']), outputOptions: expect.any(Array), @@ -2334,7 +2329,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2354,7 +2349,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2386,7 +2381,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2410,7 +2405,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2436,7 +2431,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2455,7 +2450,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD129', @@ -2476,7 +2471,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2498,7 +2493,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel vaapi', @@ -2523,7 +2518,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_output_format vaapi', '-threads 1']), outputOptions: expect.arrayContaining([ @@ -2546,7 +2541,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_output_format vaapi', '-threads 1']), outputOptions: expect.arrayContaining([expect.stringContaining('format=nv12')]), @@ -2565,7 +2560,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_device /dev/dri/renderD129']), outputOptions: expect.any(Array), @@ -2584,7 +2579,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(2); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2607,7 +2602,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(3); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264']), @@ -2624,7 +2619,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(2); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264']), @@ -2649,7 +2644,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel rkmpp', @@ -2689,7 +2684,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([`-c:v hevc_rkmpp`, '-level 153', '-rc_mode AVBR', '-b:v 10000k']), @@ -2706,7 +2701,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([`-c:v h264_rkmpp`, '-level 51', '-rc_mode CQP', '-qp_init 30']), @@ -2723,7 +2718,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([ @@ -2745,7 +2740,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([ @@ -2764,7 +2759,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: [], outputOptions: expect.arrayContaining([ @@ -2786,7 +2781,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2805,7 +2800,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2824,7 +2819,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2843,7 +2838,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy', '-vf format=yuv420p']), @@ -2858,7 +2853,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy', '-vf scale=-2:720,format=yuv420p']), @@ -2874,19 +2869,15 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).toHaveBeenCalledWith(assetStub.video.originalPath, { countFrames: true }); - expect(mocks.media.transcode).toHaveBeenCalledWith( - assetStub.video.originalPath, - 'upload/encoded-video/user-id/as/se/asset-id.mp4', - { - inputOptions: expect.any(Array), - outputOptions: expect.any(Array), - twoPass: false, - progress: { - frameCount: probeStub.videoStream2160p.videoStreams[0].frameCount, - percentInterval: expect.any(Number), - }, + expect(mocks.media.transcode).toHaveBeenCalledWith(assetStub.video.originalPath, expect.any(String), { + inputOptions: expect.any(Array), + outputOptions: expect.any(Array), + twoPass: false, + progress: { + frameCount: probeStub.videoStream2160p.videoStreams[0].frameCount, + percentInterval: expect.any(Number), }, - ); + }); }); it('should not count frames for progress when log level is not debug', async () => { @@ -2904,7 +2895,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + APP_MEDIA_LOCATION + '/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:a copy']), diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 77f0b50a0a..cc0956b9a8 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -587,7 +587,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -645,7 +645,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -703,7 +703,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 0ddf3d69b1..06ddd32601 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -28,7 +28,7 @@ describe(ServerService.name, () => { diskUseRaw: 300, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); it('should return the disk space as KiB', async () => { @@ -44,7 +44,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); it('should return the disk space as MiB', async () => { @@ -60,7 +60,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); it('should return the disk space as GiB', async () => { @@ -80,7 +80,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); it('should return the disk space as TiB', async () => { @@ -100,7 +100,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); it('should return the disk space as PiB', async () => { @@ -120,7 +120,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); }); }); diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 2751651dbf..882ffcd328 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,5 +1,6 @@ import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { AssetPathType, JobStatus } from 'src/enum'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { albumStub } from 'test/fixtures/album.stub'; @@ -110,8 +111,10 @@ describe(StorageTemplateService.name, () => { it('should migrate single moving picture', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newMotionPicturePath = `upload/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; - const newStillPicturePath = `upload/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; + const newMotionPicturePath = + APP_MEDIA_LOCATION + `/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; + const newStillPicturePath = + APP_MEDIA_LOCATION + `/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(stillAsset); mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(motionAsset); @@ -156,7 +159,9 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -177,7 +182,9 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -211,7 +218,9 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -234,7 +243,9 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + newPath: + APP_MEDIA_LOCATION + + `/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -244,8 +255,9 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset(); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = + APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; + const newPath = APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === asset.originalPath)); mocks.move.getByEntity.mockResolvedValue({ @@ -284,8 +296,9 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset({ fileSizeInByte: 5000 }); - const previousFailedNewPath = `upload/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; - const newPath = `upload/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = + APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; + const newPath = APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -319,7 +332,8 @@ describe(StorageTemplateService.name, () => { it('should fail move if copying and hash of asset and the new file do not match', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newPath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; + const newPath = + APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -409,7 +423,7 @@ describe(StorageTemplateService.name, () => { it('should handle an asset with a duplicate destination', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; const newPath2 = newPath.replace('.jpg', '+1.jpg'); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -466,7 +480,7 @@ describe(StorageTemplateService.name, () => { it('should move an asset', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); mocks.move.create.mockResolvedValue({ @@ -502,18 +516,20 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, - originalPath: `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + originalPath: expect.stringContaining( + `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + ), }); }); it('should copy the file if rename fails due to EXDEV (rename across filesystems)', async () => { const asset = assetStub.storageAsset({ originalPath: '/path/to/original.jpg', fileSizeInByte: 5000 }); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -572,14 +588,14 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.copyFile).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.stat).toHaveBeenCalledWith( - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -603,7 +619,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -631,8 +647,8 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - `upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`, + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -657,8 +673,8 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -683,8 +699,8 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); @@ -709,8 +725,8 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), + expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); }); diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 0e051f2642..567b78ac09 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -32,18 +32,36 @@ describe(StorageService.name, () => { upload: true, }, }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/encoded-video'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/library'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/profile'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/upload'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/backups'); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/encoded-video/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/library/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/profile/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/thumbs/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/upload/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/backups/.immich', expect.any(Buffer)); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/encoded-video')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/thumbs')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/encoded-video/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/library/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/profile/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/thumbs/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/upload/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/backups/.immich'), + expect.any(Buffer), + ); }); it('should enable mount folder checking for a new folder type', async () => { @@ -71,11 +89,17 @@ describe(StorageService.name, () => { }, }); expect(mocks.storage.mkdirSync).toHaveBeenCalledTimes(2); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/library'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/backups'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); expect(mocks.storage.createFile).toHaveBeenCalledTimes(2); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/library/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/backups/.immich', expect.any(Buffer)); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/library/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('upload/backups/.immich'), + expect.any(Buffer), + ); }); it('should throw an error if .immich is missing', async () => { @@ -131,7 +155,7 @@ describe(StorageService.name, () => { await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.systemMetadata.set).not.toHaveBeenCalled(); + expect(mocks.systemMetadata.set).not.toHaveBeenCalledWith(SystemMetadataKey.SystemFlags, expect.anything()); }); }); diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 6e3ef4820a..632e0c1385 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum'; @@ -60,6 +61,17 @@ export class StorageService extends BaseService { } } }); + + await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { + const current = APP_MEDIA_LOCATION; + const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); + const previous = savedValue?.location || ''; + + if (previous !== current) { + this.logger.log(`Media location changed (from=${previous}, to=${current})`); + await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + } + }); } @OnJob({ name: JobName.FileDelete, queue: QueueName.BackgroundTask }) @@ -87,9 +99,8 @@ export class StorageService extends BaseService { try { await this.storageRepository.readFile(internalPath); } catch (error) { - const fullyQualifiedPath = resolve(process.cwd(), internalPath); - this.logger.error(`Failed to read ${fullyQualifiedPath} (${internalPath}): ${error}`); - throw new ImmichStartupError(`Failed to read: "${externalPath} (${fullyQualifiedPath}) - ${docsMessage}"`); + this.logger.error(`Failed to read (${internalPath}): ${error}`); + throw new ImmichStartupError(`Failed to read: "${externalPath} (${internalPath}) - ${docsMessage}"`); } } diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index 3a85389ace..b4e616974e 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -235,11 +235,26 @@ describe(UserService.name, () => { await sut.handleUserDelete({ id: user.id }); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/library/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/upload/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/profile/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/thumbs/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/encoded-video/deleted-user', options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('upload/library/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('upload/upload/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('upload/profile/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('upload/thumbs/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('upload/encoded-video/deleted-user'), + options, + ); expect(mocks.album.deleteAll).toHaveBeenCalledWith(user.id); expect(mocks.user.delete).toHaveBeenCalledWith(user, true); }); @@ -253,7 +268,7 @@ describe(UserService.name, () => { const options = { force: true, recursive: true }; - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/library/admin', options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('upload/library/admin'), options); }); }); diff --git a/server/src/types.ts b/server/src/types.ts index 79aeedd47d..9cd1aa996b 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -451,11 +451,13 @@ export type MemoriesState = { /** memories have already been created through this date */ lastOnThisDayDate: string; }; +export type MediaLocation = { location: string }; export interface SystemMetadata extends Record> { [SystemMetadataKey.AdminOnboarding]: { isOnboarded: boolean }; [SystemMetadataKey.FacialRecognitionState]: { lastRun?: string }; [SystemMetadataKey.License]: { licenseKey: string; activationKey: string; activatedAt: Date }; + [SystemMetadataKey.MediaLocation]: MediaLocation; [SystemMetadataKey.ReverseGeocodingState]: { lastUpdate?: string; lastImportFileName?: string }; [SystemMetadataKey.SystemConfig]: DeepPartial; [SystemMetadataKey.SystemFlags]: DeepPartial; diff --git a/server/src/utils/file.ts b/server/src/utils/file.ts index 3e1a1b7f68..2331a45a62 100644 --- a/server/src/utils/file.ts +++ b/server/src/utils/file.ts @@ -1,7 +1,7 @@ import { HttpException, StreamableFile } from '@nestjs/common'; import { NextFunction, Response } from 'express'; import { access, constants } from 'node:fs/promises'; -import { basename, extname, isAbsolute } from 'node:path'; +import { basename, extname } from 'node:path'; import { promisify } from 'node:util'; import { CacheControl } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -62,15 +62,9 @@ export const sendFile = async ( res.header('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(file.fileName)}`); } - // configure options for serving - const options: SendFileOptions = { dotfiles: 'allow' }; - if (!isAbsolute(file.path)) { - options.root = process.cwd(); - } - await access(file.path, constants.R_OK); - return await _sendFile(file.path, options); + return await _sendFile(file.path, { dotfiles: 'allow' }); } catch (error: Error | any) { // ignore client-closed connection if (isConnectionAborted(error) || res.headersSent) { diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index a29babbf54..6fca29d98e 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -39,5 +39,6 @@ export const newAssetRepositoryMock = (): Mocked Date: Fri, 18 Jul 2025 10:57:49 -0400 Subject: [PATCH 006/169] feat: remove dep on cwd for workers (#20012) --- .../manifest.json | 1 - server/src/main.ts | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json diff --git a/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json b/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json deleted file mode 100644 index 7391713b6f..0000000000 --- a/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json +++ /dev/null @@ -1 +0,0 @@ -{"client":{"name":"basic","version":0,"file-system":"device-agnostic","perform-ownership-analysis":"no"},"targets":{"":[""]},"commands":{"":{"tool":"phony","inputs":[""],"outputs":[""]},"P0:::Gate WorkspaceHeaderMapVFSFilesWritten":{"tool":"phony","inputs":[],"outputs":[""]}}} \ No newline at end of file diff --git a/server/src/main.ts b/server/src/main.ts index 591fc156d9..68ea396e7a 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -1,5 +1,6 @@ import { CommandFactory } from 'nest-commander'; import { ChildProcess, fork } from 'node:child_process'; +import { dirname, join } from 'node:path'; import { Worker } from 'node:worker_threads'; import { ImmichAdminModule } from 'src/app.module'; import { ImmichWorker, LogLevel } from 'src/enum'; @@ -33,14 +34,18 @@ const onExit = (name: string, exitCode: number | null) => { function bootstrapWorker(name: ImmichWorker) { console.log(`Starting ${name} worker`); + // eslint-disable-next-line unicorn/prefer-module + const basePath = dirname(__filename); + const workerFile = join(basePath, 'workers', `${name}.js`); + let worker: Worker | ChildProcess; if (name === ImmichWorker.Api) { - worker = fork(`./dist/workers/${name}.js`, [], { + worker = fork(workerFile, [], { execArgv: process.execArgv.map((arg) => (arg.startsWith('--inspect') ? '--inspect=0.0.0.0:9231' : arg)), }); apiProcess = worker; } else { - worker = new Worker(`./dist/workers/${name}.js`); + worker = new Worker(workerFile); } worker.on('error', (error) => onError(name, error)); From f33e1ad94c4fa558f78248ea4a6da0d9840e6aa4 Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Fri, 18 Jul 2025 11:19:06 -0400 Subject: [PATCH 007/169] feat: relocate scripts, PATH update (#20002) Relocate scripts, and PATH updates --- docker/docker-compose.dev.yml | 4 +- e2e/docker-compose.yml | 1 - server/Dockerfile | 6 +-- {docker/scripts => server/bin}/get-cpus.sh | 0 server/bin/immich-admin | 2 +- server/bin/immich-dev | 8 +++- server/bin/start.sh | 44 ++++++++++++++++++++++ server/start.sh | 29 -------------- web/Dockerfile | 5 ++- web/bin/immich-web | 1 + 10 files changed, 60 insertions(+), 40 deletions(-) rename {docker/scripts => server/bin}/get-cpus.sh (100%) create mode 100755 server/bin/start.sh delete mode 100755 server/start.sh diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 56d1817317..f3d1e83fb2 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -16,7 +16,7 @@ name: immich-dev services: immich-server: container_name: immich_server - command: ['/usr/src/app/bin/immich-dev'] + command: ['immich-dev'] image: immich-server-dev:latest # extends: # file: hwaccel.transcoding.yml @@ -70,7 +70,7 @@ services: # user: 0:0 build: context: ../web - command: ['/usr/src/app/bin/immich-web'] + command: ['immich-web'] env_file: - .env ports: diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 6ec5402360..26c3951278 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -3,7 +3,6 @@ name: immich-e2e services: immich-server: container_name: immich-e2e-server - command: ['./start.sh'] image: immich-server:latest build: context: ../ diff --git a/server/Dockerfile b/server/Dockerfile index 0b3b864c95..b95887f18b 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -12,7 +12,7 @@ ENV PATH="${PATH}:/usr/src/app/bin" \ IMMICH_ENV=development \ NVIDIA_DRIVER_CAPABILITIES=all \ NVIDIA_VISIBLE_DEVICES=all -ENTRYPOINT ["tini", "--", "/bin/sh"] +ENTRYPOINT ["tini", "--", "/bin/sh", "-c"] FROM dev AS dev-container-server @@ -110,8 +110,6 @@ COPY --from=prod /usr/src/app/bin ./bin COPY --from=web /usr/src/app/build /build/www COPY server/resources resources COPY server/package.json server/package-lock.json ./ -COPY server/start*.sh ./ -COPY "docker/scripts/get-cpus.sh" ./ RUN npm install -g @immich/cli && npm cache clean --force COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE @@ -134,7 +132,7 @@ ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE VOLUME /usr/src/app/upload EXPOSE 2283 -ENTRYPOINT ["tini", "--", "/bin/bash"] +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] CMD ["start.sh"] HEALTHCHECK CMD immich-healthcheck diff --git a/docker/scripts/get-cpus.sh b/server/bin/get-cpus.sh similarity index 100% rename from docker/scripts/get-cpus.sh rename to server/bin/get-cpus.sh diff --git a/server/bin/immich-admin b/server/bin/immich-admin index 30fd33a20a..0465a362b8 100755 --- a/server/bin/immich-admin +++ b/server/bin/immich-admin @@ -1,3 +1,3 @@ #!/usr/bin/env sh -/usr/src/app/start.sh immich-admin "$@" +start.sh immich-admin "$@" diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 177455d037..85d75b8b0c 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -1,3 +1,9 @@ #!/usr/bin/env bash -node /usr/src/app/node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@" +if [ "$IMMICH_ENV" != "development" ]; then + echo "This command can only be run in development environments" + exit 1 +fi + +cd /usr/src/app || exit 1 +node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/bin/start.sh b/server/bin/start.sh new file mode 100755 index 0000000000..7c6069570d --- /dev/null +++ b/server/bin/start.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +echo "Initializing Immich $IMMICH_SOURCE_REF" + +lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" +if [ -f "$lib_path" ]; then + export LD_PRELOAD="$lib_path" +else + echo "skipping libmimalloc - path not found $lib_path" +fi +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" +SERVER_HOME=/usr/src/app + +read_file_and_export() { + if [ -n "${!1}" ]; then + content="$(cat "${!1}")" + export "$2"="${content}" + unset "$1" + fi +} +read_file_and_export "DB_URL_FILE" "DB_URL" +read_file_and_export "DB_HOSTNAME_FILE" "DB_HOSTNAME" +read_file_and_export "DB_DATABASE_NAME_FILE" "DB_DATABASE_NAME" +read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME" +read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD" +read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD" + +if CPU_CORES="${CPU_CORES:=$(get-cpus.sh 2>/dev/null)}"; then + echo "Detected CPU Cores: $CPU_CORES" + if [ "$CPU_CORES" -gt 4 ]; then + export UV_THREADPOOL_SIZE=$CPU_CORES + fi +else + echo "skipping get-cpus.sh - not found in PATH or failed: using default UV_THREADPOOL_SIZE" +fi + +if [ -f "${SERVER_HOME}/dist/main.js" ]; then + exec node "${SERVER_HOME}/dist/main.js" "$@" +else + echo "Error: ${SERVER_HOME}/dist/main.js not found" + if [ "$IMMICH_ENV" = "development" ]; then + echo "You may need to build the server first." + fi + exit 1 +fi diff --git a/server/start.sh b/server/start.sh deleted file mode 100755 index 1a08d01a75..0000000000 --- a/server/start.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -echo "Initializing Immich $IMMICH_SOURCE_REF" - -lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" -export LD_PRELOAD="$lib_path" -export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" - -read_file_and_export() { - if [ -n "${!1}" ]; then - content="$(cat "${!1}")" - export "$2"="${content}" - unset "$1" - fi -} -read_file_and_export "DB_URL_FILE" "DB_URL" -read_file_and_export "DB_HOSTNAME_FILE" "DB_HOSTNAME" -read_file_and_export "DB_DATABASE_NAME_FILE" "DB_DATABASE_NAME" -read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME" -read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD" -read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD" - -export CPU_CORES="${CPU_CORES:=$(./get-cpus.sh)}" -echo "Detected CPU Cores: $CPU_CORES" -if [ "$CPU_CORES" -gt 4 ]; then - export UV_THREADPOOL_SIZE=$CPU_CORES -fi - -exec node /usr/src/app/dist/main "$@" diff --git a/web/Dockerfile b/web/Dockerfile index 1c6c4b46bf..c83f783391 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -5,7 +5,8 @@ USER node WORKDIR /usr/src/app COPY --chown=node:node package*.json ./ RUN npm ci -ENV CHOKIDAR_USEPOLLING=true +ENV CHOKIDAR_USEPOLLING=true \ + PATH="${PATH}:/usr/src/app/bin" EXPOSE 24678 EXPOSE 3000 -ENTRYPOINT ["/sbin/tini", "--", "/bin/sh"] +ENTRYPOINT ["/sbin/tini", "--", "/bin/sh", "-c"] diff --git a/web/bin/immich-web b/web/bin/immich-web index ea748863db..8868945ee1 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -5,6 +5,7 @@ TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk npm --prefix "$TYPESCRIPT_SDK" install npm --prefix "$TYPESCRIPT_SDK" run build +cd /usr/src/app || exit 1 COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" From 9719965caf7d54b7ee492f9e528b83f949bfff25 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Fri, 18 Jul 2025 11:54:15 -0500 Subject: [PATCH 008/169] fix: invalid android manifest (#20015) fix extra > in android manifest --- mobile/android/app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 3aa72d2876..09276f6d4a 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -171,7 +171,6 @@ - > From 635f5de186afef2feb7a8e26c80cda2cb068aefa Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:24:35 +0530 Subject: [PATCH 009/169] chore: change dcm constraint to include 1.30.0 (#20017) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/dcm_global.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/dcm_global.yaml b/mobile/dcm_global.yaml index d2465e64b6..c33846e674 100644 --- a/mobile/dcm_global.yaml +++ b/mobile/dcm_global.yaml @@ -1 +1 @@ -version: '>=1.29.0 <1.30.0' +version: '>=1.29.0 <=1.30.0' From dcfe8d5aded526227514257014d8e5f40d00a921 Mon Sep 17 00:00:00 2001 From: megumin Date: Fri, 18 Jul 2025 17:55:24 +0100 Subject: [PATCH 010/169] fix: send filename when viewing the original file (#20005) * feat: add fileName to downloadOriginal response * test: add fileName to ImmichFileResponse for downloadOriginal * lint: use single quotes for fileName string in test --- server/src/services/asset-media.service.spec.ts | 1 + server/src/services/asset-media.service.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 08b42b6cbf..0585a159ac 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -505,6 +505,7 @@ describe(AssetMediaService.name, () => { await expect(sut.downloadOriginal(authStub.admin, 'asset-1')).resolves.toEqual( new ImmichFileResponse({ path: '/original/path.jpg', + fileName: 'asset-id.jpg', contentType: 'image/jpeg', cacheControl: CacheControl.PrivateWithCache, }), diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 080774d038..517a1f665f 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -197,6 +197,7 @@ export class AssetMediaService extends BaseService { return new ImmichFileResponse({ path: asset.originalPath, + fileName: asset.originalFileName, contentType: mimeTypes.lookup(asset.originalPath), cacheControl: CacheControl.PrivateWithCache, }); From 5d244c6feca7010a64ac78a1e3dc1364ef989955 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Jul 2025 13:16:22 -0500 Subject: [PATCH 011/169] chore: finish drift locked page (#20013) * feat: overlay mechanism * handle merged asset local id extraction * locked view asset viewer actions * pr feedback --- .../pages/library/locked/pin_auth.page.dart | 20 ++++++-- .../presentation/pages/drift_album.page.dart | 4 ++ .../pages/drift_locked_folder.page.dart | 51 ++++++++++++++++--- .../asset_viewer/asset_viewer.page.dart | 9 ++-- .../asset_viewer/bottom_sheet.widget.dart | 7 ++- .../asset_viewer/top_app_bar.widget.dart | 16 +++++- .../infrastructure/action.provider.dart | 20 +++++--- .../lib/routing/app_navigation_observer.dart | 25 ++++++++- mobile/lib/routing/router.dart | 2 +- mobile/lib/services/action.service.dart | 14 ++++- .../common/mesmerizing_sliver_app_bar.dart | 1 - 11 files changed, 143 insertions(+), 26 deletions(-) diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index cca0e3b7ac..9bfd96ed74 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -1,13 +1,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' show useState; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/local_auth.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/forms/pin_registration_form.dart'; import 'package:immich_mobile/widgets/forms/pin_verification_form.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; @RoutePage() class PinAuthPage extends HookConsumerWidget { @@ -19,6 +20,7 @@ class PinAuthPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final localAuthState = ref.watch(localAuthProvider); final showPinRegistrationForm = useState(createPinCode); + final isBetaTimeline = Store.isBetaTimelineEnabled; Future registerBiometric(String pinCode) async { final isRegistered = @@ -39,7 +41,11 @@ class PinAuthPage extends HookConsumerWidget { ), ); - context.replaceRoute(const LockedRoute()); + if (isBetaTimeline) { + context.replaceRoute(const DriftLockedFolderRoute()); + } else { + context.replaceRoute(const LockedRoute()); + } } } @@ -93,8 +99,14 @@ class PinAuthPage extends HookConsumerWidget { Center( child: PinVerificationForm( autoFocus: true, - onSuccess: (_) => - context.replaceRoute(const LockedRoute()), + onSuccess: (_) { + if (isBetaTimeline) { + context + .replaceRoute(const DriftLockedFolderRoute()); + } else { + context.replaceRoute(const LockedRoute()); + } + }, ), ), const SizedBox(height: 24), diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index fbdf1ef116..c7dffbeaef 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -95,9 +95,13 @@ class _DriftAlbumsPageState extends ConsumerState { return RefreshIndicator( onRefresh: onRefresh, + edgeOffset: 100, child: CustomScrollView( slivers: [ ImmichSliverAppBar( + snap: false, + floating: false, + pinned: true, actions: [ IconButton( icon: const Icon( diff --git a/mobile/lib/presentation/pages/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/drift_locked_folder.page.dart index 9b42cdb103..e134b418e9 100644 --- a/mobile/lib/presentation/pages/drift_locked_folder.page.dart +++ b/mobile/lib/presentation/pages/drift_locked_folder.page.dart @@ -4,14 +4,45 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() -class DriftLockedFolderPage extends StatelessWidget { +class DriftLockedFolderPage extends ConsumerStatefulWidget { const DriftLockedFolderPage({super.key}); + @override + ConsumerState createState() => + _DriftLockedFolderPageState(); +} + +class _DriftLockedFolderPageState extends ConsumerState + with WidgetsBindingObserver { + bool _showOverlay = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (mounted) { + setState(() { + _showOverlay = state != AppLifecycleState.resumed; + }); + } + } + @override Widget build(BuildContext context) { return ProviderScope( @@ -30,12 +61,18 @@ class DriftLockedFolderPage extends StatelessWidget { }, ), ], - child: Timeline( - appBar: MesmerizingSliverAppBar( - title: 'locked_folder'.t(context: context), - ), - bottomSheet: const LockedFolderBottomSheet(), - ), + child: _showOverlay + ? const SizedBox() + : PopScope( + onPopInvokedWithResult: (didPop, _) => + didPop ? ref.read(authProvider.notifier).lockPinCode() : null, + child: Timeline( + appBar: MesmerizingSliverAppBar( + title: 'locked_folder'.t(context: context), + ), + bottomSheet: const LockedFolderBottomSheet(), + ), + ), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 50f4a09197..9356c2f43e 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -25,6 +25,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart'; import 'package:platform/platform.dart'; @@ -638,6 +639,8 @@ class _AssetViewerState extends ConsumerState { }); }); + final isInLockedView = ref.watch(inLockedViewProvider); + // Currently it is not possible to scroll the asset when the bottom sheet is open all the way. // Issue: https://github.com/flutter/flutter/issues/109037 // TODO: Add a custom scrum builder once the fix lands on stable @@ -666,13 +669,13 @@ class _AssetViewerState extends ConsumerState { ), bottomNavigationBar: showingBottomSheet ? const SizedBox.shrink() - : const Column( + : Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - AssetStackRow(), - ViewerBottomBar(), + const AssetStackRow(), + if (!isInLockedView) const ViewerBottomBar(), ], ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index ea6dece942..a8b7c79588 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -18,6 +18,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_ import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; @@ -44,6 +45,8 @@ class AssetDetailBottomSheet extends ConsumerWidget { serverInfoProvider.select((state) => state.serverFeatures.trash), ); + final isInLockedView = ref.watch(inLockedViewProvider); + final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.hasRemote) ...[ @@ -63,8 +66,10 @@ class AssetDetailBottomSheet extends ConsumerWidget { ], ]; + final lockedViewActions = []; + return BaseBottomSheet( - actions: actions, + actions: isInLockedView ? lockedViewActions : actions, slivers: const [_AssetDetailBottomSheet()], controller: controller, initialChildSize: initialChildSize, diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index c85c4390ae..4cdf9f2287 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_act import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; @@ -27,6 +28,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; + final isInLockedView = ref.watch(inLockedViewProvider); final isShowingSheet = ref .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); @@ -62,6 +64,14 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { const _KebabMenu(), ]; + final lockedViewActions = [ + if (isCasting || (asset.hasRemote && websocketConnected)) + const CastActionButton( + menuItem: true, + ), + const _KebabMenu(), + ]; + return IgnorePointer( ignoring: opacity < 255, child: AnimatedOpacity( @@ -74,7 +84,11 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { iconTheme: const IconThemeData(size: 22, color: Colors.white), actionsIconTheme: const IconThemeData(size: 22, color: Colors.white), shape: const Border(), - actions: isShowingSheet ? null : actions, + actions: isShowingSheet + ? null + : isInLockedView + ? lockedViewActions + : actions, ), ), ); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 456b072a39..a0ebf448fc 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -47,7 +47,18 @@ class ActionNotifier extends Notifier { } List _getLocalIdsForSource(ActionSource source) { - return _getIdsForSource(source).toIds().toList(growable: false); + final Set assets = _getAssets(source); + final List localIds = []; + + for (final asset in assets) { + if (asset is LocalAsset) { + localIds.add(asset.id); + } else if (asset is RemoteAsset && asset.localId != null) { + localIds.add(asset.localId!); + } + } + + return localIds; } List _getOwnedRemoteIdsForSource(ActionSource source) { @@ -162,8 +173,9 @@ class ActionNotifier extends Notifier { Future moveToLockFolder(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); try { - await _service.moveToLockFolder(ids); + await _service.moveToLockFolder(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to move assets to lock folder', error, stack); @@ -329,7 +341,3 @@ extension on Iterable { return whereType().where((a) => a.ownerId == ownerId); } } - -extension on Iterable { - Iterable toIds() => map((e) => e.id); -} diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 047e897c8e..98560018ee 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -25,7 +25,7 @@ class AppNavigationObserver extends AutoRouterObserver { @override void didPush(Route route, Route? previousRoute) { _handleLockedViewState(route, previousRoute); - + _handleDriftLockedFolderState(route, previousRoute); Future( () => ref.read(currentRouteNameProvider.notifier).state = route.settings.name, @@ -54,4 +54,27 @@ class AppNavigationObserver extends AutoRouterObserver { ); } } + + _handleDriftLockedFolderState(Route route, Route? previousRoute) { + final isInLockedView = ref.read(inLockedViewProvider); + final isFromLockedViewToDetailView = + route.settings.name == AssetViewerRoute.name && + previousRoute?.settings.name == DriftLockedFolderRoute.name; + + final isFromDetailViewToInfoPanelView = route.settings.name == null && + previousRoute?.settings.name == AssetViewerRoute.name && + isInLockedView; + + if (route.settings.name == DriftLockedFolderRoute.name || + isFromLockedViewToDetailView || + isFromDetailViewToInfoPanelView) { + Future( + () => ref.read(inLockedViewProvider.notifier).state = true, + ); + } else { + Future( + () => ref.read(inLockedViewProvider.notifier).state = false, + ); + } + } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 94e3437d66..3a694df816 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -427,7 +427,7 @@ class AppRouter extends RootStackRouter { ), AutoRoute( page: DriftLockedFolderRoute.page, - guards: [_authGuard, _duplicateGuard], + guards: [_authGuard, _lockedGuard, _duplicateGuard], ), AutoRoute( page: DriftVideoRoute.page, diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index adefd5da16..b59df5b3dc 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -83,7 +83,10 @@ class ActionService { ); } - Future moveToLockFolder(List remoteIds) async { + Future moveToLockFolder( + List remoteIds, + List localIds, + ) async { await _assetApiRepository.updateVisibility( remoteIds, AssetVisibilityEnum.locked, @@ -92,6 +95,15 @@ class ActionService { remoteIds, AssetVisibility.locked, ); + + // Ask user if they want to delete local copies + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } Future removeFromLockFolder(List remoteIds) async { diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index eace57fe5c..eecc099a9e 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -22,7 +22,6 @@ class MesmerizingSliverAppBar extends ConsumerStatefulWidget { final String title; final IconData icon; - @override ConsumerState createState() => _MesmerizingSliverAppBarState(); From 9e94f52b057005e33cd16927e1f4f344ce1db009 Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Fri, 18 Jul 2025 17:56:26 -0400 Subject: [PATCH 012/169] chore: dockerfile layout changes (#19673) Dockerfile layout changes Fix up web path feat: update server env vars for layout --- .dockerignore | 32 ++++++------- .gitignore | 1 + docker/docker-compose.dev.yml | 17 +++---- server/Dockerfile | 88 +++++++++++++++++------------------ server/bin/immich-dev | 4 +- server/bin/start.sh | 2 +- web/Dockerfile | 13 ++++-- web/bin/immich-web | 6 +-- 8 files changed, 85 insertions(+), 78 deletions(-) diff --git a/.dockerignore b/.dockerignore index d152800d1b..f7efb5c56e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,41 +1,41 @@ .vscode/ .github/ .git/ +.env* +*.log +*.tmp +*.temp + +**/Dockerfile +**/node_modules/ +**/.pnpm-store/ +**/dist/ +**/coverage/ +**/build/ design/ docker/ -Dockerfile !docker/scripts + docs/ !docs/package.json !docs/package-lock.json + e2e/ !e2e/package.json !e2e/package-lock.json + fastlane/ machine-learning/ misc/ mobile/ -cli/coverage/ -cli/dist/ -cli/node_modules/ -cli/Dockerfile - open-api/typescript-sdk/build/ -open-api/typescript-sdk/node_modules/ +!open-api/typescript-sdk/package.json +!open-api/typescript-sdk/package-lock.json -server/coverage/ -server/node_modules/ server/upload/ server/src/queries -server/dist/ server/www/ -server/Dockerfile -web/node_modules/ -web/coverage/ web/.svelte-kit -web/build/ -web/.env -web/Dockerfile diff --git a/.gitignore b/.gitignore index b4ebd04841..af85d96c02 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ mobile/android/fastlane/report.xml mobile/ios/fastlane/report.xml vite.config.js.timestamp-* +.pnpm-store diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index f3d1e83fb2..32ff115102 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -27,11 +27,11 @@ services: target: dev restart: unless-stopped volumes: - - ../server:/usr/src/app - - ../open-api:/usr/src/open-api + - ../server:/usr/src/app/server + - ../open-api:/usr/src/app/open-api - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload - ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload - - /usr/src/app/node_modules + - /usr/src/app/server/node_modules - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -69,7 +69,8 @@ services: # Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919 # user: 0:0 build: - context: ../web + context: ../ + dockerfile: web/Dockerfile command: ['immich-web'] env_file: - .env @@ -77,11 +78,11 @@ services: - 3000:3000 - 24678:24678 volumes: - - ../web:/usr/src/app - - ../i18n:/usr/src/i18n - - ../open-api/:/usr/src/open-api/ + - ../web:/usr/src/app/web + - ../i18n:/usr/src/app/i18n + - ../open-api/:/usr/src/app/open-api/ # - ../../ui:/usr/ui - - /usr/src/app/node_modules + - /usr/src/app/web/node_modules ulimits: nofile: soft: 1048576 diff --git a/server/Dockerfile b/server/Dockerfile index b95887f18b..e082d0e69e 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -2,25 +2,26 @@ FROM ghcr.io/immich-app/base-server-dev:202507162011@sha256:85d4230c2208646bd6c528db41b2213d780b11b7a311397ca6a2aaba7cf697c8 AS dev WORKDIR /usr/src/app -COPY server/package.json server/package-lock.json ./ +COPY ./server/package* ./server/ +WORKDIR /usr/src/app/server RUN npm ci && \ - # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need - # they're marked as optional dependencies, so we need to copy them manually after pruning - rm -rf node_modules/@img/sharp-libvips* && \ - rm -rf node_modules/@img/sharp-linuxmusl-x64 -ENV PATH="${PATH}:/usr/src/app/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -ENTRYPOINT ["tini", "--", "/bin/sh", "-c"] + # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need + # they're marked as optional dependencies, so we need to copy them manually after pruning + rm -rf node_modules/@img/sharp-libvips* && \ + rm -rf node_modules/@img/sharp-linuxmusl-x64 +ENV PATH="${PATH}:/usr/src/app/server/bin" \ + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] FROM dev AS dev-container-server RUN rm -rf /usr/src/app RUN apt-get update && \ - apt-get install sudo inetutils-ping openjdk-11-jre-headless \ - vim nano \ - -y --no-install-recommends --fix-missing + apt-get install sudo inetutils-ping openjdk-11-jre-headless \ + vim nano \ + -y --no-install-recommends --fix-missing RUN usermod -aG sudo node RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers @@ -38,14 +39,14 @@ FROM dev-container-server AS dev-container-mobile USER root # Enable multiarch for arm64 if necessary RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ - dpkg --add-architecture amd64 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64; \ - fi + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + fi # Flutter SDK # https://flutter.dev/docs/development/tools/sdk/releases?tab=linux @@ -77,43 +78,42 @@ FROM dev AS prod COPY server . RUN npm run build RUN npm prune --omit=dev --omit=optional -COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img -COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl +COPY --from=dev /usr/src/app/server/node_modules/@img ./node_modules/@img +COPY --from=dev /usr/src/app/server/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web -WORKDIR /usr/src/open-api/typescript-sdk -COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ -RUN npm ci -COPY open-api/typescript-sdk/ ./ -RUN npm run build - WORKDIR /usr/src/app -COPY web/package*.json web/svelte.config.js ./ -RUN npm ci -COPY web ./ -COPY i18n ../i18n -RUN npm run build +COPY ./web ./web/ +COPY ./i18n ./i18n/ +COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/ +WORKDIR /usr/src/app/open-api/typescript-sdk +RUN npm ci && npm run build + +WORKDIR /usr/src/app/web +RUN npm ci && npm run build # prod build FROM ghcr.io/immich-app/base-server-prod:202507162011@sha256:636f3ddb6106628ef851d51c23f3fa2c6e4829390cc315b27b38c288c82b23a7 WORKDIR /usr/src/app ENV NODE_ENV=production \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -COPY --from=prod /usr/src/app/node_modules ./node_modules -COPY --from=prod /usr/src/app/dist ./dist -COPY --from=prod /usr/src/app/bin ./bin -COPY --from=web /usr/src/app/build /build/www -COPY server/resources resources -COPY server/package.json server/package-lock.json ./ -RUN npm install -g @immich/cli && npm cache clean --force + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all + +COPY --from=prod /usr/src/app/server/node_modules ./server/node_modules +COPY --from=prod /usr/src/app/server/dist ./server/dist +COPY --from=prod /usr/src/app/server/bin ./server/bin +COPY --from=web /usr/src/app/web/build /build/www +COPY ./server/resources ./server/resources +COPY ./server/package.json server/package-lock.json ./ COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE -ENV PATH="${PATH}:/usr/src/app/bin" + +RUN npm install -g @immich/cli && npm cache clean --force +ENV PATH="${PATH}:/usr/src/app/server/bin" ARG BUILD_ID ARG BUILD_IMAGE diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 85d75b8b0c..533c10ef9d 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -5,5 +5,5 @@ if [ "$IMMICH_ENV" != "development" ]; then exit 1 fi -cd /usr/src/app || exit 1 -node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@" +cd /usr/src/app/server || exit 1 +npm exec nest start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/bin/start.sh b/server/bin/start.sh index 7c6069570d..2b4351a6bc 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -8,7 +8,7 @@ else echo "skipping libmimalloc - path not found $lib_path" fi export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" -SERVER_HOME=/usr/src/app +SERVER_HOME=/usr/src/app/server read_file_and_export() { if [ -n "${!1}" ]; then diff --git a/web/Dockerfile b/web/Dockerfile index c83f783391..3c119fdd4d 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,12 +1,17 @@ FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e -RUN apk add --no-cache tini +RUN apk add --no-cache tini bash + USER node WORKDIR /usr/src/app -COPY --chown=node:node package*.json ./ + +COPY --chown=node:node ./web/package* ./web/ + +WORKDIR /usr/src/app/web RUN npm ci + ENV CHOKIDAR_USEPOLLING=true \ - PATH="${PATH}:/usr/src/app/bin" + PATH="${PATH}:/usr/src/app/web/bin" EXPOSE 24678 EXPOSE 3000 -ENTRYPOINT ["/sbin/tini", "--", "/bin/sh", "-c"] +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] diff --git a/web/bin/immich-web b/web/bin/immich-web index 8868945ee1..d2739cf6c3 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -1,11 +1,11 @@ #!/usr/bin/env sh -TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk +TYPESCRIPT_SDK=/usr/src/app/open-api/typescript-sdk npm --prefix "$TYPESCRIPT_SDK" install npm --prefix "$TYPESCRIPT_SDK" run build -cd /usr/src/app || exit 1 +cd /usr/src/app/web || exit 1 COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" @@ -19,4 +19,4 @@ done echo "Connected to $UPSTREAM" -node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000 +npx vite dev --host 0.0.0.0 --port 3000 From f929dc0816e1167cd15f0b8e2231e3e44113557a Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Fri, 18 Jul 2025 19:07:49 -0400 Subject: [PATCH 013/169] fix: devcontainer layout (#20021) --- .devcontainer/mobile/container-compose-overrides.yml | 4 ++-- .devcontainer/server/container-compose-overrides.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index 62a97a01eb..0543f42317 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -11,8 +11,8 @@ services: - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - server_node_modules:/workspaces/immich/server/node_modules - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload - - ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload + - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload + - ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload - /etc/localtime:/etc/localtime:ro database: diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index d7efc92cb1..24ac9734b1 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -13,8 +13,8 @@ services: - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - server_node_modules:/workspaces/immich/server/node_modules - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload - - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload + - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/usr/src/app/upload + - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/usr/src/app/upload/upload - /etc/localtime:/etc/localtime:ro immich-web: From fafb88d31c357d180052143773926d03d3e2256b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Jul 2025 23:58:53 -0500 Subject: [PATCH 014/169] feat(mobile): new upload (#18726) --- mobile/lib/constants/constants.dart | 5 + .../domain/services/local_album.service.dart | 4 + .../repositories/backup.repository.dart | 154 +++++++++ .../repositories/local_album.repository.dart | 28 +- .../repositories/local_asset.repository.dart | 7 + .../repositories/storage.repository.dart | 48 ++- mobile/lib/main.dart | 23 +- .../upload/share_intent_attachment.model.dart | 2 +- .../lib/pages/backup/drift_backup.page.dart | 294 +++++++++++++++++ .../drift_backup_album_selection.page.dart | 307 ++++++++++++++++++ .../pages/common/change_experience.page.dart | 6 + .../pages/share_intent/share_intent.page.dart | 2 +- .../pages/dev/feat_in_development.page.dart | 27 +- .../providers/app_life_cycle.provider.dart | 3 + .../share_intent_upload.provider.dart | 36 +- .../backup/backup_album.provider.dart | 62 ++++ .../backup/drift_backup.provider.dart | 194 +++++++++++ mobile/lib/providers/websocket.provider.dart | 42 ++- .../lib/repositories/upload.repository.dart | 25 +- mobile/lib/routing/router.dart | 10 + mobile/lib/routing/router.gr.dart | 32 ++ mobile/lib/services/drift_backup.service.dart | 286 ++++++++++++++++ mobile/lib/services/upload.service.dart | 83 +++-- mobile/lib/utils/database.utils.dart | 31 ++ mobile/lib/utils/upload.dart | 1 - .../backup/drift_album_info_list_tile.dart | 121 +++++++ .../widgets/common/immich_sliver_app_bar.dart | 2 +- 27 files changed, 1733 insertions(+), 102 deletions(-) create mode 100644 mobile/lib/infrastructure/repositories/backup.repository.dart create mode 100644 mobile/lib/pages/backup/drift_backup.page.dart create mode 100644 mobile/lib/pages/backup/drift_backup_album_selection.page.dart create mode 100644 mobile/lib/providers/backup/backup_album.provider.dart create mode 100644 mobile/lib/providers/backup/drift_backup.provider.dart create mode 100644 mobile/lib/services/drift_backup.service.dart create mode 100644 mobile/lib/utils/database.utils.dart delete mode 100644 mobile/lib/utils/upload.dart create mode 100644 mobile/lib/widgets/backup/drift_album_info_list_tile.dart diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index c37498ea3e..37a3eec073 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -16,6 +16,11 @@ const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys const String kSecuredPinCode = "secured_pin_code"; +// background_downloader task groups +const String kManualUploadGroup = 'manual_upload_group'; +const String kBackupGroup = 'backup_group'; +const String kBackupLivePhotoGroup = 'backup_live_photo_group'; + // Timeline constants const int kTimelineNoneSegmentSize = 120; const int kTimelineAssetLoadBatchSize = 256; diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 9af12ce595..7ec9231196 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -14,4 +14,8 @@ class LocalAlbumService { Future getThumbnail(String albumId) { return _repository.getThumbnail(albumId); } + + Future update(LocalAlbum album) { + return _repository.upsert(album); + } } diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart new file mode 100644 index 0000000000..4ce3b07e8b --- /dev/null +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -0,0 +1,154 @@ +import 'package:drift/drift.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import "package:immich_mobile/utils/database.utils.dart"; + +final backupRepositoryProvider = Provider( + (ref) => DriftBackupRepository(ref.watch(driftProvider)), +); + +class DriftBackupRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftBackupRepository(this._db) : super(_db); + + _getExcludedSubquery() { + return _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection + .equalsValue(BackupSelection.excluded), + ); + } + + Future getTotalCount() async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection + .equalsValue(BackupSelection.selected) & + _db.localAlbumAssetEntity.assetId + .isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future getRemainderCount() async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns( + [_db.localAlbumAssetEntity.assetId], + ) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + innerJoin( + _db.localAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum + .equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection + .equalsValue(BackupSelection.selected) & + _db.remoteAssetEntity.id.isNull() & + _db.localAlbumAssetEntity.assetId + .isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future getBackupCount() async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns( + [_db.localAlbumAssetEntity.assetId], + ) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + innerJoin( + _db.localAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + innerJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum + .equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection + .equalsValue(BackupSelection.selected) & + _db.remoteAssetEntity.id.isNotNull() & + _db.localAlbumAssetEntity.assetId + .isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future> getCandidates() async { + final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumEntity.id]) + ..where( + _db.localAlbumEntity.backupSelection + .equalsValue(BackupSelection.selected), + ); + + final query = _db.localAssetEntity.select() + ..where( + (lae) => + existsQuery( + _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..where( + _db.localAlbumAssetEntity.albumId + .isInQuery(selectedAlbumIds) & + _db.localAlbumAssetEntity.assetId.equalsExp(lae.id), + ), + ) & + notExistsQuery( + _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.checksum]) + ..where( + _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + lae.checksum.isNotNull(), + ), + ) & + lae.id.isNotInQuery(_getExcludedSubquery()), + ); + + return query.map((localAsset) => localAsset.toDto()).get(); + } +} diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 44ebe7f7ca..ba9dfd979d 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/utils/database.utils.dart'; import 'package:platform/platform.dart'; enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum } @@ -381,30 +382,3 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return results.isNotEmpty ? results.first : null; } } - -extension on LocalAlbumEntityData { - LocalAlbum toDto({int assetCount = 0}) { - return LocalAlbum( - id: id, - name: name, - updatedAt: updatedAt, - assetCount: assetCount, - backupSelection: backupSelection, - ); - } -} - -extension on LocalAssetEntityData { - LocalAsset toDto() { - return LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 31a11f7047..8d21c858a2 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -56,4 +56,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { } }); } + + Future getById(String id) { + final query = _db.localAssetEntity.select() + ..where((lae) => lae.id.equals(id)); + + return query.map((row) => row.toDto()).getSingleOrNull(); + } } diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 5b511709cd..0cf4f20ba8 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:logging/logging.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -7,8 +8,9 @@ class StorageRepository { const StorageRepository(); Future getFileForAsset(String assetId) async { - final log = Logger('StorageRepository'); File? file; + final log = Logger('StorageRepository'); + try { final entity = await AssetEntity.fromId(assetId); file = await entity?.originFile; @@ -20,4 +22,48 @@ class StorageRepository { } return file; } + + Future getMotionFileForAsset(LocalAsset asset) async { + File? file; + final log = Logger('StorageRepository'); + + try { + final entity = await AssetEntity.fromId(asset.id); + file = await entity?.originFileWithSubtype; + if (file == null) { + log.warning( + "Cannot get motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + ); + } + } catch (error, stackTrace) { + log.warning( + "Error getting motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + error, + stackTrace, + ); + } + return file; + } + + Future getAssetEntityForAsset(LocalAsset asset) async { + final log = Logger('StorageRepository'); + + AssetEntity? entity; + + try { + entity = await AssetEntity.fromId(asset.id); + if (entity == null) { + log.warning( + "Cannot get AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + ); + } + } catch (error, stackTrace) { + log.warning( + "Error getting AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + error, + stackTrace, + ); + } + return entity; + } } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f67767f767..acadf4c887 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -93,6 +93,13 @@ Future initApp() async { initializeTimeZones(); + // Initialize the file downloader + + await FileDownloader().configure( + // maxConcurrent: 5, maxConcurrentByHost: 2, maxConcurrentByGroup: 3 + globalConfig: (Config.holdingQueue, (5, 2, 3)), + ); + await FileDownloader().trackTasksInGroup( downloadGroupLivePhoto, markDownloadedComplete: false, @@ -171,7 +178,21 @@ class ImmichAppState extends ConsumerState } void _configureFileDownloaderNotifications() { - FileDownloader().configureNotification( + FileDownloader().configureNotificationForGroup( + downloadGroupImage, + running: TaskNotification( + 'downloading_media'.tr(), + '${'file_name'.tr()}: {filename}', + ), + complete: TaskNotification( + 'download_finished'.tr(), + '${'file_name'.tr()}: {filename}', + ), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + downloadGroupVideo, running: TaskNotification( 'downloading_media'.tr(), '${'file_name'.tr()}: {filename}', diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 1bdb5b6b48..7e57cf94d2 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -17,7 +17,7 @@ enum UploadStatus { notFound, failed, canceled, - waitingtoRetry, + waitingToRetry, paused, } diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart new file mode 100644 index 0000000000..34649ca42c --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -0,0 +1,294 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; + +@RoutePage() +class DriftBackupPage extends HookConsumerWidget { + const DriftBackupPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + useEffect( + () { + ref.read(driftBackupProvider.notifier).getBackupStatus(); + return null; + }, + [], + ); + + Widget buildControlButtons() { + return Padding( + padding: const EdgeInsets.only( + top: 24, + ), + child: Column( + children: [ + ElevatedButton( + onPressed: () => ref.read(driftBackupProvider.notifier).backup(), + child: const Text( + "backup_controller_page_start_backup", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + OutlinedButton( + onPressed: () => ref.read(driftBackupProvider.notifier).cancel(), + child: const Text( + "cancel", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + OutlinedButton( + onPressed: () => + ref.read(driftBackupProvider.notifier).getDataInfo(), + child: const Text( + "Get database info", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + ], + ), + ); + } + + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text( + "Backup (Experimental)", + ), + leading: IconButton( + onPressed: () { + ref.watch(websocketProvider.notifier).listenUploadEvent(); + context.maybePop(true); + }, + splashRadius: 24, + icon: const Icon( + Icons.arrow_back_ios_rounded, + ), + ), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: IconButton( + onPressed: () => context.pushRoute(const BackupOptionsRoute()), + splashRadius: 24, + icon: const Icon( + Icons.settings_outlined, + ), + ), + ), + ], + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 16.0, + right: 16, + bottom: 32, + ), + child: ListView( + children: [ + const SizedBox(height: 8), + const _BackupAlbumSelectionCard(), + const _TotalCard(), + const _BackupCard(), + const _RemainderCard(), + const Divider(), + buildControlButtons(), + ], + ), + ), + ], + ), + ); + } +} + +class _BackupAlbumSelectionCard extends ConsumerWidget { + const _BackupAlbumSelectionCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Widget buildSelectedAlbumName() { + String text = "backup_controller_page_backup_selected".tr(); + final albums = ref + .watch(backupAlbumProvider) + .where( + (album) => album.backupSelection == BackupSelection.selected, + ) + .toList(); + + if (albums.isNotEmpty) { + for (var album in albums) { + if (album.name == "Recent" || album.name == "Recents") { + text += "${album.name} (${'all'.tr()}), "; + } else { + text += "${album.name}, "; + } + } + + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + text.trim().substring(0, text.length - 2), + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + ), + ), + ); + } else { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + "backup_controller_page_none_selected".tr(), + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + ), + ), + ); + } + } + + Widget buildExcludedAlbumName() { + String text = "backup_controller_page_excluded".tr(); + final albums = ref + .watch(backupAlbumProvider) + .where( + (album) => album.backupSelection == BackupSelection.excluded, + ) + .toList(); + + if (albums.isNotEmpty) { + for (var album in albums) { + text += "${album.name}, "; + } + + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + text.trim().substring(0, text.length - 2), + style: context.textTheme.labelLarge?.copyWith( + color: Colors.red[300], + ), + ), + ); + } else { + return const SizedBox(); + } + } + + return Card( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide( + color: context.colorScheme.outlineVariant, + width: 1, + ), + ), + elevation: 0, + borderOnForeground: false, + child: ListTile( + minVerticalPadding: 18, + title: Text( + "backup_controller_page_albums", + style: context.textTheme.titleMedium, + ).tr(), + subtitle: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "backup_controller_page_to_backup", + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ).tr(), + buildSelectedAlbumName(), + buildExcludedAlbumName(), + ], + ), + ), + trailing: ElevatedButton( + onPressed: () async { + await context.pushRoute(const DriftBackupAlbumSelectionRoute()); + ref.read(driftBackupProvider.notifier).getBackupStatus(); + }, + child: const Text( + "select", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + ), + ); + } +} + +class _TotalCard extends ConsumerWidget { + const _TotalCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final totalCount = + ref.watch(driftBackupProvider.select((p) => p.totalCount)); + + return BackupInfoCard( + title: "total".tr(), + subtitle: "backup_controller_page_total_sub".tr(), + info: totalCount.toString(), + ); + } +} + +class _BackupCard extends ConsumerWidget { + const _BackupCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final backupCount = + ref.watch(driftBackupProvider.select((p) => p.backupCount)); + + return BackupInfoCard( + title: "backup_controller_page_backup".tr(), + subtitle: "backup_controller_page_backup_sub".tr(), + info: backupCount.toString(), + ); + } +} + +class _RemainderCard extends ConsumerWidget { + const _RemainderCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final remainderCount = + ref.watch(driftBackupProvider.select((p) => p.remainderCount)); + return BackupInfoCard( + title: "backup_controller_page_remainder".tr(), + subtitle: "backup_controller_page_remainder_sub".tr(), + info: remainderCount.toString(), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart new file mode 100644 index 0000000000..2e7a2d4c2d --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -0,0 +1,307 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; + +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; +import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; + +@RoutePage() +class DriftBackupAlbumSelectionPage extends HookConsumerWidget { + const DriftBackupAlbumSelectionPage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final albums = ref.watch(backupAlbumProvider); + + final selectedBackupAlbums = albums + .where((album) => album.backupSelection == BackupSelection.selected) + .toList(); + final excludedBackupAlbums = albums + .where((album) => album.backupSelection == BackupSelection.excluded) + .toList(); + final enableSyncUploadAlbum = + useAppSettingsState(AppSettingsEnum.syncAlbums); + final isDarkTheme = context.isDarkTheme; + + useEffect( + () { + ref.watch(backupProvider.notifier).getBackupInfo(); + ref.watch(backupAlbumProvider.notifier).getAll(); + return null; + }, + [], + ); + + buildAlbumSelectionList() { + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + ((context, index) { + return DriftAlbumInfoListTile( + album: albums[index], + ); + }), + childCount: albums.length, + ), + ), + ); + } + + buildAlbumSelectionGrid() { + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.all(12.0), + sliver: SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + ), + itemCount: albums.length, + itemBuilder: ((context, index) { + return DriftAlbumInfoListTile( + album: albums[index], + ); + }), + ), + ); + } + + buildSelectedAlbumNameChip() { + return selectedBackupAlbums.map((album) { + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: GestureDetector( + onTap: removeSelection, + child: Chip( + label: Text( + album.name, + style: TextStyle( + fontSize: 12, + color: isDarkTheme ? Colors.black : Colors.white, + fontWeight: FontWeight.bold, + ), + ), + backgroundColor: context.primaryColor, + deleteIconColor: isDarkTheme ? Colors.black : Colors.white, + deleteIcon: const Icon( + Icons.cancel_rounded, + size: 15, + ), + onDeleted: removeSelection, + ), + ), + ); + }).toSet(); + } + + buildExcludedAlbumNameChip() { + return excludedBackupAlbums.map((album) { + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return GestureDetector( + onTap: removeSelection, + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Chip( + label: Text( + album.name, + style: TextStyle( + fontSize: 12, + color: context.scaffoldBackgroundColor, + fontWeight: FontWeight.bold, + ), + ), + backgroundColor: Colors.red[300], + deleteIconColor: context.scaffoldBackgroundColor, + deleteIcon: const Icon( + Icons.cancel_rounded, + size: 15, + ), + onDeleted: removeSelection, + ), + ), + ); + }).toSet(); + } + + handleSyncAlbumToggle(bool isEnable) async { + if (isEnable) { + await ref.read(albumProvider.notifier).refreshRemoteAlbums(); + for (final album in selectedBackupAlbums) { + await ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } + } + } + + return Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () => context.maybePop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + title: const Text( + "backup_album_selection_page_select_albums", + ).tr(), + elevation: 0, + ), + body: SafeArea( + child: CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + child: Text( + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, + ).tr(), + ), + // Selected Album Chips + + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + ...buildSelectedAlbumNameChip(), + ...buildExcludedAlbumNameChip(), + ], + ), + ), + + SettingsSwitchListTile( + valueNotifier: enableSyncUploadAlbum, + title: "sync_albums".tr(), + subtitle: "sync_upload_album_setting_subtitle".tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.primary, + ), + onChanged: handleSyncAlbumToggle, + ), + + ListTile( + title: Text( + "backup_album_selection_page_albums_device".tr( + namedArgs: { + 'count': ref + .watch(backupProvider) + .availableAlbums + .length + .toString(), + }, + ), + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + ), + ).tr(), + ), + trailing: IconButton( + splashRadius: 16, + icon: Icon( + Icons.info, + size: 20, + color: context.primaryColor, + ), + onPressed: () { + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(10)), + ), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: context.primaryColor, + ), + ).tr(), + content: SingleChildScrollView( + child: ListBody( + children: [ + const Text( + 'backup_album_selection_page_assets_scatter', + style: TextStyle( + fontSize: 14, + ), + ).tr(), + ], + ), + ), + ); + }, + ); + }, + ), + ), + + // buildSearchBar(), + ], + ), + ), + SliverLayoutBuilder( + builder: (context, constraints) { + if (constraints.crossAxisExtent > 600) { + return buildAlbumSelectionGrid(); + } else { + return buildAlbumSelectionList(); + } + }, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 74c3116962..5d298edb42 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -42,6 +43,9 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } + ref.read(websocketProvider.notifier).stopListenToOldEvents(); + ref.read(websocketProvider.notifier).startListeningToBetaEvents(); + final permission = await ref .read(galleryPermissionNotifier.notifier) .requestGalleryPermission(); @@ -55,6 +59,8 @@ class _ChangeExperiencePageState extends ConsumerState { } } else { await ref.read(backgroundSyncProvider).cancel(); + ref.read(websocketProvider.notifier).stopListeningToBetaEvents(); + ref.read(websocketProvider.notifier).startListeningToOldEvents(); } if (mounted) { diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index 11c114e4a6..299ffe5497 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -268,7 +268,7 @@ class UploadStatusIcon extends StatelessWidget { color: Colors.red, semanticLabel: 'canceled'.tr(), ), - UploadStatus.waitingtoRetry || UploadStatus.paused => Icon( + UploadStatus.waitingToRetry || UploadStatus.paused => Icon( Icons.pause_circle_rounded, color: context.primaryColor, semanticLabel: 'paused'.tr(), diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index e94329acd2..7ee151f94d 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -91,6 +91,10 @@ final _features = [ ), _Feature( name: 'Clear Local Data', + style: const TextStyle( + color: Colors.orange, + fontWeight: FontWeight.bold, + ), icon: Icons.delete_forever_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -101,6 +105,10 @@ final _features = [ ), _Feature( name: 'Clear Remote Data', + style: const TextStyle( + color: Colors.orange, + fontWeight: FontWeight.bold, + ), icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -117,17 +125,29 @@ final _features = [ ), _Feature( name: 'Local Media Summary', + style: const TextStyle( + color: Colors.indigo, + fontWeight: FontWeight.bold, + ), icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), _Feature( name: 'Remote Media Summary', + style: const TextStyle( + color: Colors.indigo, + fontWeight: FontWeight.bold, + ), icon: Icons.summarize_rounded, onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), ), _Feature( name: 'Reset Sqlite', icon: Icons.table_view_rounded, + style: const TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), onTap: (_, ref) async { final drift = ref.read(driftProvider); // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member @@ -160,7 +180,10 @@ class FeatInDevPage extends StatelessWidget { final feat = _features[index]; return Consumer( builder: (ctx, ref, _) => ListTile( - title: Text(feat.name), + title: Text( + feat.name, + style: feat.style, + ), trailing: Icon(feat.icon), visualDensity: VisualDensity.compact, onTap: () => unawaited(feat.onTap(ctx, ref)), @@ -183,10 +206,12 @@ class _Feature { required this.name, required this.icon, required this.onTap, + this.style, }); final String name; final IconData icon; + final TextStyle? style; final Future Function(BuildContext, WidgetRef _) onTap; } diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 997058d763..5984160241 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -69,6 +69,9 @@ class AppLifeCycleNotifier extends StateNotifier { } await _ref.read(serverInfoProvider.notifier).getServerVersion(); + + // TODO: Need to decide on how we want to handle uploads once the app is resumed + // await FileDownloader().start(); } if (!Store.isBetaTimelineEnabled) { diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index ed2c485b13..3c448b112f 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; -import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; @@ -30,7 +29,7 @@ class ShareIntentUploadStateNotifier this._uploadService, this._shareIntentService, ) : super([]) { - _uploadService.onUploadStatus = _uploadStatusCallback; + _uploadService.onUploadStatus = _updateUploadStatus; _uploadService.onTaskProgress = _taskProgressCallback; } @@ -69,8 +68,8 @@ class ShareIntentUploadStateNotifier state = []; } - void _updateUploadStatus(TaskStatusUpdate task, TaskStatus status) async { - if (status == TaskStatus.canceled) { + void _updateUploadStatus(TaskStatusUpdate task) async { + if (task.status == TaskStatus.canceled) { return; } @@ -83,7 +82,7 @@ class ShareIntentUploadStateNotifier TaskStatus.running => UploadStatus.running, TaskStatus.paused => UploadStatus.paused, TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingtoRetry + TaskStatus.waitingToRetry => UploadStatus.waitingToRetry }; state = [ @@ -95,27 +94,6 @@ class ShareIntentUploadStateNotifier ]; } - void _uploadStatusCallback(TaskStatusUpdate update) { - _updateUploadStatus(update, update.status); - - switch (update.status) { - case TaskStatus.complete: - if (update.responseStatusCode == 200) { - if (kDebugMode) { - debugPrint("[COMPLETE] ${update.task.taskId} - DUPLICATE"); - } - } else { - if (kDebugMode) { - debugPrint("[COMPLETE] ${update.task.taskId}"); - } - } - break; - - default: - break; - } - } - void _taskProgressCallback(TaskProgressUpdate update) { // Ignore if the task is canceled or completed if (update.progress == downloadFailed || @@ -134,10 +112,6 @@ class ShareIntentUploadStateNotifier } Future upload(File file) { - return _uploadService.upload(file); - } - - Future cancelUpload(String id) { - return _uploadService.cancelUpload(id); + return _uploadService.buildUploadTask(file, group: kManualUploadGroup); } } diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart new file mode 100644 index 0000000000..b36d3ac57e --- /dev/null +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -0,0 +1,62 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/services/local_album.service.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; + +final backupAlbumProvider = + StateNotifierProvider>( + (ref) => BackupAlbumNotifier( + ref.watch(localAlbumServiceProvider), + ), +); + +class BackupAlbumNotifier extends StateNotifier> { + BackupAlbumNotifier(this._localAlbumService) : super([]) { + getAll(); + } + + final LocalAlbumService _localAlbumService; + + Future getAll() async { + state = await _localAlbumService.getAll(); + } + + Future selectAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.selected); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => currentAlbum.id == album.id + ? currentAlbum.copyWith(backupSelection: BackupSelection.selected) + : currentAlbum, + ) + .toList(); + } + + Future deselectAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.none); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => currentAlbum.id == album.id + ? currentAlbum.copyWith(backupSelection: BackupSelection.none) + : currentAlbum, + ) + .toList(); + } + + Future excludeAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.excluded); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => currentAlbum.id == album.id + ? currentAlbum.copyWith(backupSelection: BackupSelection.excluded) + : currentAlbum, + ) + .toList(); + } +} diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart new file mode 100644 index 0000000000..2544d208c4 --- /dev/null +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -0,0 +1,194 @@ +import 'dart:async'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/services/drift_backup.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; + +class DriftUploadStatus { + final String taskId; + final String filename; + final double progress; + + const DriftUploadStatus({ + required this.taskId, + required this.filename, + required this.progress, + }); + + DriftUploadStatus copyWith({ + String? taskId, + String? filename, + double? progress, + }) { + return DriftUploadStatus( + taskId: taskId ?? this.taskId, + filename: filename ?? this.filename, + progress: progress ?? this.progress, + ); + } + + @override + String toString() => + 'ExpUploadStatus(taskId: $taskId, filename: $filename, progress: $progress)'; + + @override + bool operator ==(covariant DriftUploadStatus other) { + if (identical(this, other)) return true; + + return other.taskId == taskId && + other.filename == filename && + other.progress == progress; + } + + @override + int get hashCode => taskId.hashCode ^ filename.hashCode ^ progress.hashCode; +} + +class DriftBackupState { + final int totalCount; + final int backupCount; + final int remainderCount; + final Map uploadItems; + + const DriftBackupState({ + required this.totalCount, + required this.backupCount, + required this.remainderCount, + required this.uploadItems, + }); + + DriftBackupState copyWith({ + int? totalCount, + int? backupCount, + int? remainderCount, + Map? uploadItems, + }) { + return DriftBackupState( + totalCount: totalCount ?? this.totalCount, + backupCount: backupCount ?? this.backupCount, + remainderCount: remainderCount ?? this.remainderCount, + uploadItems: uploadItems ?? this.uploadItems, + ); + } + + @override + String toString() { + return 'ExpBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, uploadItems: $uploadItems)'; + } + + @override + bool operator ==(covariant DriftBackupState other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other.totalCount == totalCount && + other.backupCount == backupCount && + other.remainderCount == remainderCount && + mapEquals(other.uploadItems, uploadItems); + } + + @override + int get hashCode { + return totalCount.hashCode ^ + backupCount.hashCode ^ + remainderCount.hashCode ^ + uploadItems.hashCode; + } +} + +final driftBackupProvider = + StateNotifierProvider((ref) { + return ExpBackupNotifier( + ref.watch(driftBackupServiceProvider), + ref.watch(uploadServiceProvider), + ); +}); + +class ExpBackupNotifier extends StateNotifier { + ExpBackupNotifier( + this._backupService, + this._uploadService, + ) : super( + const DriftBackupState( + totalCount: 0, + backupCount: 0, + remainderCount: 0, + uploadItems: {}, + ), + ) { + { + _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); + _uploadService.taskProgressStream.listen(_handleTaskProgressUpdate); + } + } + + final DriftBackupService _backupService; + final UploadService _uploadService; + StreamSubscription? _statusSubscription; + StreamSubscription? _progressSubscription; + + void _handleTaskStatusUpdate(TaskStatusUpdate update) { + switch (update.status) { + case TaskStatus.complete: + state = state.copyWith( + backupCount: state.backupCount + 1, + remainderCount: state.remainderCount - 1, + ); + break; + + default: + break; + } + } + + void _handleTaskProgressUpdate(TaskProgressUpdate update) {} + + Future getBackupStatus() async { + final [totalCount, backupCount, remainderCount] = await Future.wait([ + _backupService.getTotalCount(), + _backupService.getBackupCount(), + _backupService.getRemainderCount(), + ]); + + state = state.copyWith( + totalCount: totalCount, + backupCount: backupCount, + remainderCount: remainderCount, + ); + } + + Future backup() { + return _backupService.backup(); + } + + Future cancel() async { + await _backupService.cancel(); + await getDataInfo(); + } + + Future getDataInfo() async { + final a = await FileDownloader().database.allRecordsWithStatus( + TaskStatus.enqueued, + group: kBackupGroup, + ); + + final b = await FileDownloader().allTasks( + group: kBackupGroup, + ); + + debugPrint( + "Enqueued tasks: ${a.length}, All tasks: ${b.length}", + ); + } + + @override + void dispose() { + _statusSubscription?.cancel(); + _progressSubscription?.cancel(); + super.dispose(); + } +} diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index d9db831776..05fe5a087c 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -176,16 +176,14 @@ class WebsocketNotifier extends StateNotifier { ); }); - socket.on('on_upload_success', _handleOnUploadSuccess); + if (!Store.isBetaTimelineEnabled) { + startListeningToOldEvents(); + } else { + startListeningToBetaEvents(); + } + socket.on('on_config_update', _handleOnConfigUpdate); - socket.on('on_asset_delete', _handleOnAssetDelete); - socket.on('on_asset_trash', _handleOnAssetTrash); - socket.on('on_asset_restore', _handleServerUpdates); - socket.on('on_asset_update', _handleServerUpdates); - socket.on('on_asset_stack_update', _handleServerUpdates); - socket.on('on_asset_hidden', _handleOnAssetHidden); socket.on('on_new_release', _handleReleaseUpdates); - socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); } catch (e) { debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); } @@ -213,6 +211,34 @@ class WebsocketNotifier extends StateNotifier { state.socket?.off(eventName); } + void stopListenToOldEvents() { + state.socket?.off('on_upload_success'); + state.socket?.off('on_asset_delete'); + state.socket?.off('on_asset_trash'); + state.socket?.off('on_asset_restore'); + state.socket?.off('on_asset_update'); + state.socket?.off('on_asset_stack_update'); + state.socket?.off('on_asset_hidden'); + } + + void startListeningToOldEvents() { + state.socket?.on('on_upload_success', _handleOnUploadSuccess); + state.socket?.on('on_asset_delete', _handleOnAssetDelete); + state.socket?.on('on_asset_trash', _handleOnAssetTrash); + state.socket?.on('on_asset_restore', _handleServerUpdates); + state.socket?.on('on_asset_update', _handleServerUpdates); + state.socket?.on('on_asset_stack_update', _handleServerUpdates); + state.socket?.on('on_asset_hidden', _handleOnAssetHidden); + } + + void stopListeningToBetaEvents() { + state.socket?.off('AssetUploadReadyV1'); + } + + void startListeningToBetaEvents() { + state.socket?.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); + } + void listenUploadEvent() { debugPrint("Start listening to event on_upload_success"); state.socket?.on('on_upload_success', _handleOnUploadSuccess); diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 4f840fa3c6..b98eece656 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -1,6 +1,6 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/utils/upload.dart'; +import 'package:immich_mobile/constants/constants.dart'; final uploadRepositoryProvider = Provider((ref) => UploadRepository()); @@ -11,25 +11,30 @@ class UploadRepository { UploadRepository() { FileDownloader().registerCallbacks( - group: uploadGroup, + group: kBackupGroup, + taskStatusCallback: (update) => onUploadStatus?.call(update), + taskProgressCallback: (update) => onTaskProgress?.call(update), + ); + FileDownloader().registerCallbacks( + group: kBackupLivePhotoGroup, taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); } - Future upload(UploadTask task) { - return FileDownloader().enqueue(task); + void enqueueAll(List tasks) { + FileDownloader().enqueueAll(tasks); } - Future deleteAllTrackingRecords() { - return FileDownloader().database.deleteAllRecords(); + Future deleteAllTrackingRecords(String group) { + return FileDownloader().database.deleteAllRecords(group: group); } - Future cancel(String id) { - return FileDownloader().cancelTaskWithId(id); + Future cancelAll(String group) { + return FileDownloader().cancelAll(group: group); } - Future deleteRecordsWithIds(List ids) { - return FileDownloader().database.deleteRecordsWithIds(ids); + Future reset(String group) { + return FileDownloader().reset(group: group); } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 3a694df816..d7dd45dbd9 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -22,6 +22,8 @@ import 'package:immich_mobile/pages/album/album_shared_user_selection.page.dart' import 'package:immich_mobile/pages/album/album_viewer.page.dart'; import 'package:immich_mobile/pages/albums/albums.page.dart'; import 'package:immich_mobile/pages/backup/album_preview.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; @@ -385,6 +387,14 @@ class AppRouter extends RootStackRouter { page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: DriftBackupRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftBackupAlbumSelectionRoute.page, + guards: [_authGuard, _duplicateGuard], + ), AutoRoute( page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard], diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 716d5ef89a..c72dc62765 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -726,6 +726,38 @@ class DriftAssetSelectionTimelineRouteArgs { } } +/// generated route for +/// [DriftBackupAlbumSelectionPage] +class DriftBackupAlbumSelectionRoute extends PageRouteInfo { + const DriftBackupAlbumSelectionRoute({List? children}) + : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); + + static const String name = 'DriftBackupAlbumSelectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupAlbumSelectionPage(); + }, + ); +} + +/// generated route for +/// [DriftBackupPage] +class DriftBackupRoute extends PageRouteInfo { + const DriftBackupRoute({List? children}) + : super(DriftBackupRoute.name, initialChildren: children); + + static const String name = 'DriftBackupRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupPage(); + }, + ); +} + /// generated route for /// [DriftCreateAlbumPage] class DriftCreateAlbumRoute extends PageRouteInfo { diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart new file mode 100644 index 0000000000..5966aad304 --- /dev/null +++ b/mobile/lib/services/drift_backup.service.dart @@ -0,0 +1,286 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/services/upload.service.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +final driftBackupServiceProvider = Provider( + (ref) => DriftBackupService( + ref.watch(backupRepositoryProvider), + ref.watch(storageRepositoryProvider), + ref.watch(uploadServiceProvider), + ref.watch(localAssetRepository), + ), +); + +class DriftBackupService { + DriftBackupService( + this._backupRepository, + this._storageRepository, + this._uploadService, + this._localAssetRepository, + ) { + _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); + } + + final DriftBackupRepository _backupRepository; + final StorageRepository _storageRepository; + final DriftLocalAssetRepository _localAssetRepository; + final UploadService _uploadService; + final _log = Logger("DriftBackupService"); + + bool shouldCancel = false; + + Future getTotalCount() { + return _backupRepository.getTotalCount(); + } + + Future getRemainderCount() { + return _backupRepository.getRemainderCount(); + } + + Future getBackupCount() { + return _backupRepository.getBackupCount(); + } + + Future backup() async { + shouldCancel = false; + + final candidates = await _backupRepository.getCandidates(); + if (candidates.isEmpty) { + return; + } + + const batchSize = 100; + int count = 0; + for (int i = 0; i < candidates.length; i += batchSize) { + if (shouldCancel) { + break; + } + + final batch = candidates.skip(i).take(batchSize).toList(); + + List tasks = []; + for (final asset in batch) { + final task = await _getUploadTask(asset); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty && !shouldCancel) { + count += tasks.length; + _uploadService.enqueueTasks(tasks); + debugPrint( + "Enqueued $count/${candidates.length} tasks for backup", + ); + } + } + } + + void _handleTaskStatusUpdate(TaskStatusUpdate update) { + switch (update.status) { + case TaskStatus.complete: + _handleLivePhoto(update); + break; + + default: + break; + } + } + + Future _handleLivePhoto(TaskStatusUpdate update) async { + try { + if (update.task.metaData.isEmpty || update.task.metaData == '') { + return; + } + + final metadata = UploadTaskMetadata.fromJson(update.task.metaData); + if (!metadata.isLivePhotos) { + return; + } + + if (update.responseBody == null || update.responseBody!.isEmpty) { + return; + } + final response = jsonDecode(update.responseBody!); + + final localAsset = + await _localAssetRepository.getById(metadata.localAssetId); + if (localAsset == null) { + return; + } + + final uploadTask = await _getLivePhotoUploadTask( + localAsset, + response['id'] as String, + ); + + if (uploadTask == null) { + return; + } + + _uploadService.enqueueTasks([uploadTask]); + } catch (error, stackTrace) { + _log.severe("Error handling live photo upload task", error, stackTrace); + debugPrint("Error handling live photo upload task: $error $stackTrace"); + } + } + + Future _getUploadTask(LocalAsset asset) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + File? file; + + /// iOS LivePhoto has two files: a photo and a video. + /// They are uploaded separately, with video file being upload first, then returned with the assetId + /// The assetId is then used as a metadata for the photo file upload task. + /// + /// We implement two separate upload groups for this, the normal one for the video file + /// and the higher priority group for the photo file because the video file is already uploaded. + /// + /// The cancel operation will only cancel the video group (normal group), the photo group will not + /// be touched, as the video file is already uploaded. + + if (entity.isLivePhoto) { + file = await _storageRepository.getMotionFileForAsset(asset); + } else { + file = await _storageRepository.getFileForAsset(asset.id); + } + + if (file == null) { + return null; + } + + final originalFileName = entity.isLivePhoto + ? p.setExtension( + asset.name, + p.extension(file.path), + ) + : asset.name; + + String metadata = UploadTaskMetadata( + localAssetId: asset.id, + isLivePhotos: entity.isLivePhoto, + livePhotoVideoId: '', + ).toJson(); + + return _uploadService.buildUploadTask( + file, + originalFileName: originalFileName, + deviceAssetId: asset.id, + metadata: metadata, + group: kBackupGroup, + ); + } + + Future _getLivePhotoUploadTask( + LocalAsset asset, + String livePhotoVideoId, + ) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + final file = await _storageRepository.getFileForAsset(asset.id); + if (file == null) { + return null; + } + + final fields = { + 'livePhotoVideoId': livePhotoVideoId, + }; + + return _uploadService.buildUploadTask( + file, + originalFileName: asset.name, + deviceAssetId: asset.id, + fields: fields, + group: kBackupLivePhotoGroup, + priority: 0, + ); + } + + Future cancel() async { + shouldCancel = true; + await _uploadService.cancelAllForGroup(kBackupGroup); + } +} + +class UploadTaskMetadata { + final String localAssetId; + final bool isLivePhotos; + final String livePhotoVideoId; + + const UploadTaskMetadata({ + required this.localAssetId, + required this.isLivePhotos, + required this.livePhotoVideoId, + }); + + UploadTaskMetadata copyWith({ + String? localAssetId, + bool? isLivePhotos, + String? livePhotoVideoId, + }) { + return UploadTaskMetadata( + localAssetId: localAssetId ?? this.localAssetId, + isLivePhotos: isLivePhotos ?? this.isLivePhotos, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + ); + } + + Map toMap() { + return { + 'localAssetId': localAssetId, + 'isLivePhotos': isLivePhotos, + 'livePhotoVideoId': livePhotoVideoId, + }; + } + + factory UploadTaskMetadata.fromMap(Map map) { + return UploadTaskMetadata( + localAssetId: map['localAssetId'] as String, + isLivePhotos: map['isLivePhotos'] as bool, + livePhotoVideoId: map['livePhotoVideoId'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory UploadTaskMetadata.fromJson(String source) => + UploadTaskMetadata.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'UploadTaskMetadata(localAssetId: $localAssetId, isLivePhotos: $isLivePhotos, livePhotoVideoId: $livePhotoVideoId)'; + + @override + bool operator ==(covariant UploadTaskMetadata other) { + if (identical(this, other)) return true; + + return other.localAssetId == localAssetId && + other.isLivePhotos == isLivePhotos && + other.livePhotoVideoId == livePhotoVideoId; + } + + @override + int get hashCode => + localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode; +} diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 18f90ab844..60aab4a16c 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; @@ -6,22 +7,28 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/upload.dart'; import 'package:path/path.dart'; -// import 'package:logging/logging.dart'; -final uploadServiceProvider = Provider( - (ref) => UploadService( - ref.watch(uploadRepositoryProvider), - ), -); +final uploadServiceProvider = Provider((ref) { + final service = UploadService(ref.watch(uploadRepositoryProvider)); + ref.onDispose(service.dispose); + return service; +}); class UploadService { final UploadRepository _uploadRepository; - // final Logger _log = Logger("UploadService"); void Function(TaskStatusUpdate)? onUploadStatus; void Function(TaskProgressUpdate)? onTaskProgress; + final StreamController _taskStatusController = + StreamController.broadcast(); + final StreamController _taskProgressController = + StreamController.broadcast(); + + Stream get taskStatusStream => _taskStatusController.stream; + Stream get taskProgressStream => + _taskProgressController.stream; + UploadService( this._uploadRepository, ) { @@ -31,29 +38,65 @@ class UploadService { void _onTaskProgressCallback(TaskProgressUpdate update) { onTaskProgress?.call(update); + if (!_taskProgressController.isClosed) { + _taskProgressController.add(update); + } } void _onUploadCallback(TaskStatusUpdate update) { onUploadStatus?.call(update); + if (!_taskStatusController.isClosed) { + _taskStatusController.add(update); + } + } + + void dispose() { + _taskStatusController.close(); + _taskProgressController.close(); } Future cancelUpload(String id) { return FileDownloader().cancelTaskWithId(id); } - Future upload(File file) async { - final task = await _buildUploadTask( - hash(file.path).toString(), - file, - ); - - await _uploadRepository.upload(task); + Future cancelAllForGroup(String group) async { + await _uploadRepository.cancelAll(group); + await _uploadRepository.reset(group); + await _uploadRepository.deleteAllTrackingRecords(group); } - Future _buildUploadTask( + void enqueueTasks(List tasks) { + _uploadRepository.enqueueAll(tasks); + } + + Future buildUploadTask( + File file, { + required String group, + Map? fields, + String? originalFileName, + String? deviceAssetId, + String? metadata, + int? priority, + }) async { + return _buildTask( + deviceAssetId ?? hash(file.path).toString(), + file, + fields: fields, + originalFileName: originalFileName, + metadata: metadata, + group: group, + priority: priority, + ); + } + + Future _buildTask( String id, File file, { + required String group, Map? fields, + String? originalFileName, + String? metadata, + int? priority, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); @@ -65,9 +108,8 @@ class UploadService { final stats = await file.stat(); final fileCreatedAt = stats.changed; final fileModifiedAt = stats.modified; - final fieldsMap = { - 'filename': filename, + 'filename': originalFileName ?? filename, 'deviceAssetId': id, 'deviceId': deviceId, 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), @@ -79,6 +121,7 @@ class UploadService { return UploadTask( taskId: id, + displayName: originalFileName ?? filename, httpRequestMethod: 'POST', url: url, headers: headers, @@ -87,7 +130,9 @@ class UploadService { baseDirectory: baseDirectory, directory: directory, fileField: 'assetData', - group: uploadGroup, + metaData: metadata ?? '', + group: group, + priority: priority ?? 5, updates: Updates.statusAndProgress, ); } diff --git a/mobile/lib/utils/database.utils.dart b/mobile/lib/utils/database.utils.dart new file mode 100644 index 0000000000..446b92db19 --- /dev/null +++ b/mobile/lib/utils/database.utils.dart @@ -0,0 +1,31 @@ +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; + +extension LocalAlbumEntityDataHelper on LocalAlbumEntityData { + LocalAlbum toDto({int assetCount = 0}) { + return LocalAlbum( + id: id, + name: name, + updatedAt: updatedAt, + assetCount: assetCount, + backupSelection: backupSelection, + ); + } +} + +extension LocalAssetEntityDataHelper on LocalAssetEntityData { + LocalAsset toDto() { + return LocalAsset( + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + ); + } +} diff --git a/mobile/lib/utils/upload.dart b/mobile/lib/utils/upload.dart deleted file mode 100644 index a0b77f1d93..0000000000 --- a/mobile/lib/utils/upload.dart +++ /dev/null @@ -1 +0,0 @@ -const uploadGroup = 'upload_group'; diff --git a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart new file mode 100644 index 0000000000..42178c972e --- /dev/null +++ b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart @@ -0,0 +1,121 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DriftAlbumInfoListTile extends HookConsumerWidget { + final LocalAlbum album; + + const DriftAlbumInfoListTile({super.key, required this.album}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isSelected = album.backupSelection == BackupSelection.selected; + final bool isExcluded = album.backupSelection == BackupSelection.excluded; + + final syncAlbum = ref + .watch(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.syncAlbums); + + buildTileColor() { + if (isSelected) { + return context.isDarkTheme + ? context.primaryColor.withAlpha(100) + : context.primaryColor.withAlpha(25); + } else if (isExcluded) { + return context.isDarkTheme + ? Colors.red[300]?.withAlpha(150) + : Colors.red[100]?.withAlpha(150); + } else { + return Colors.transparent; + } + } + + buildIcon() { + if (isSelected) { + return Icon( + Icons.check_circle_rounded, + color: context.colorScheme.primary, + ); + } + + if (isExcluded) { + return Icon( + Icons.remove_circle_rounded, + color: context.colorScheme.error, + ); + } + + return Icon( + Icons.circle, + color: context.colorScheme.surfaceContainerHighest, + ); + } + + return GestureDetector( + onDoubleTap: () { + ref.watch(hapticFeedbackProvider.notifier).selectionClick(); + + if (isExcluded) { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } else { + if (album.id == 'isAll' || album.name == 'Recents') { + ImmichToast.show( + context: context, + msg: 'Cannot exclude album contains all assets', + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + return; + } + + ref.read(backupAlbumProvider.notifier).excludeAlbum(album); + } + }, + child: ListTile( + tileColor: buildTileColor(), + contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + onTap: () { + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + if (isSelected) { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } else { + ref.read(backupAlbumProvider.notifier).selectAlbum(album); + if (syncAlbum) { + ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } + } + }, + leading: buildIcon(), + title: Text( + album.name, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(album.assetCount.toString()), + trailing: IconButton( + onPressed: () { + context.pushRoute(LocalTimelineRoute(album: album)); + }, + icon: Icon( + Icons.image_outlined, + color: context.primaryColor, + size: 24, + ), + splashRadius: 25, + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index b58a1ad6f9..2b020d20ee 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -205,7 +205,7 @@ class _BackupIndicator extends ConsumerWidget { final badgeBackground = context.colorScheme.surfaceContainer; return InkWell( - onTap: () => context.pushRoute(const BackupControllerRoute()), + onTap: () => context.pushRoute(const DriftBackupRoute()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( From 261818ddd9684884b4091a494e4d76c63d257a61 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Sat, 19 Jul 2025 12:34:17 +0300 Subject: [PATCH 015/169] refactor(mobile): download button in new timeline (#20010) * download button * minor improvements --- i18n/en.json | 1 + .../download_action_button.widget.dart | 40 ++++++- .../asset_viewer/bottom_sheet.widget.dart | 3 +- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 2 +- .../locked_folder_bottom_sheet.widget.dart | 2 +- .../partner_detail_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../infrastructure/action.provider.dart | 24 ++++- .../lib/repositories/download.repository.dart | 102 ++++++++++++++++-- mobile/lib/services/action.service.dart | 9 ++ 12 files changed, 170 insertions(+), 21 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index dfe2954c9f..77dc2e235c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -782,6 +782,7 @@ "documentation": "Documentation", "done": "Done", "download": "Download", + "download_action_prompt": "Downloading {count} assets", "download_canceled": "Download canceled", "download_complete": "Download complete", "download_enqueue": "Download enqueued", diff --git a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart index 53ea5d4946..c6eda703a5 100644 --- a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart @@ -1,16 +1,54 @@ +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class DownloadActionButton extends ConsumerWidget { - const DownloadActionButton({super.key}); + final ActionSource source; + + const DownloadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).downloadAll(source); + ref.read(multiSelectProvider.notifier).reset(); + + if (!context.mounted) { + return; + } + + if (!result.success) { + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } else if (result.count > 0) { + ImmichToast.show( + context: context, + msg: 'download_action_prompt' + .t(context: context, args: {'count': result.count.toString()}), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.success, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.download, label: "download".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index a8b7c79588..89822fef91 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -52,7 +52,8 @@ class AssetDetailBottomSheet extends ConsumerWidget { if (asset.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.viewer), const ArchiveActionButton(source: ActionSource.viewer), - if (!asset.hasLocal) const DownloadActionButton(), + if (!asset.hasLocal) + const DownloadActionButton(source: ActionSource.viewer), isTrashEnable ? const TrashActionButton(source: ActionSource.viewer) : const DeletePermanentActionButton(source: ActionSource.viewer), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index a3d24ec8ee..9ed35da4cd 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -38,7 +38,7 @@ class ArchiveBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const UnArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton( diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index eefe19194c..a1e1255a9f 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -38,7 +38,7 @@ class FavoriteBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const UnFavoriteActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton( diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index f9a9dd3203..373d264d82 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -38,7 +38,7 @@ class GeneralBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton( diff --git a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart index ef71f3a3a3..7f82f750f7 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LockedFolderBottomSheet extends ConsumerWidget { shouldCloseOnMinExtent: false, actions: [ ShareActionButton(source: ActionSource.timeline), - DownloadActionButton(), + DownloadActionButton(source: ActionSource.timeline), DeletePermanentActionButton(source: ActionSource.timeline), RemoveFromLockFolderActionButton(source: ActionSource.timeline), ], diff --git a/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart index 1cdf6f28d6..5e4dae34bc 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart @@ -16,7 +16,7 @@ class PartnerDetailBottomSheet extends ConsumerWidget { shouldCloseOnMinExtent: false, actions: [ ShareActionButton(source: ActionSource.timeline), - DownloadActionButton(), + DownloadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index b5fecdf7a4..cfb6fe4f1a 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -41,7 +41,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton( diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index a0ebf448fc..cb025ef941 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -41,7 +41,8 @@ class ActionNotifier extends Notifier { } List _getRemoteIdsForSource(ActionSource source) { - return _getIdsForSource(source) + return _getAssets(source) + .whereType() .toIds() .toList(growable: false); } @@ -63,7 +64,8 @@ class ActionNotifier extends Notifier { List _getOwnedRemoteIdsForSource(ActionSource source) { final ownerId = ref.read(currentUserProvider)?.id; - return _getIdsForSource(source) + return _getAssets(source) + .whereType() .ownedAssets(ownerId) .toIds() .toList(growable: false); @@ -331,6 +333,24 @@ class ActionNotifier extends Notifier { ); } } + + Future downloadAll(ActionSource source) async { + final assets = + _getAssets(source).whereType().toList(growable: false); + + try { + final didEnqueue = await _service.downloadAll(assets); + final enqueueCount = didEnqueue.where((e) => e).length; + return ActionResult(count: enqueueCount, success: true); + } catch (error, stack) { + _logger.severe('Failed to download assets', error, stack); + return ActionResult( + count: assets.length, + success: false, + error: error.toString(), + ); + } + } } extension on Iterable { diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 72f7e065ca..f1dae3c251 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -1,10 +1,28 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:background_downloader/background_downloader.dart'; +import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; +import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/download.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); class DownloadRepository { + static final _downloader = FileDownloader(); + static final _dummyTask = DownloadTask( + taskId: 'dummy', + url: '', + filename: 'dummy', + group: '', + updates: Updates.statusAndProgress, + ); + static final _dummyMetadata = {'part': LivePhotosPart.image, 'id': ''}; + void Function(TaskStatusUpdate)? onImageDownloadStatus; void Function(TaskStatusUpdate)? onVideoDownloadStatus; @@ -14,19 +32,19 @@ class DownloadRepository { void Function(TaskProgressUpdate)? onTaskProgress; DownloadRepository() { - FileDownloader().registerCallbacks( + _downloader.registerCallbacks( group: downloadGroupImage, taskStatusCallback: (update) => onImageDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); - FileDownloader().registerCallbacks( + _downloader.registerCallbacks( group: downloadGroupVideo, taskStatusCallback: (update) => onVideoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); - FileDownloader().registerCallbacks( + _downloader.registerCallbacks( group: downloadGroupLivePhoto, taskStatusCallback: (update) => onLivePhotoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), @@ -34,25 +52,87 @@ class DownloadRepository { } Future> downloadAll(List tasks) { - return FileDownloader().enqueueAll(tasks); + return _downloader.enqueueAll(tasks); } Future deleteAllTrackingRecords() { - return FileDownloader().database.deleteAllRecords(); + return _downloader.database.deleteAllRecords(); } Future cancel(String id) { - return FileDownloader().cancelTaskWithId(id); + return _downloader.cancelTaskWithId(id); } Future> getLiveVideoTasks() { - return FileDownloader().database.allRecordsWithStatus( - TaskStatus.complete, - group: downloadGroupLivePhoto, - ); + return _downloader.database.allRecordsWithStatus( + TaskStatus.complete, + group: downloadGroupLivePhoto, + ); } Future deleteRecordsWithIds(List ids) { - return FileDownloader().database.deleteRecordsWithIds(ids); + return _downloader.database.deleteRecordsWithIds(ids); + } + + Future> downloadAllAssets(List assets) async { + if (assets.isEmpty) { + return Future.value(const []); + } + + final length = Platform.isAndroid ? assets.length : assets.length * 2; + final tasks = List.filled(length, _dummyTask); + int taskIndex = 0; + final headers = ApiService.getRequestHeaders(); + for (final asset in assets) { + if (!asset.isRemoteOnly) { + continue; + } + + final id = asset.id; + final livePhotoVideoId = asset.livePhotoVideoId; + final isVideo = asset.isVideo; + final url = getOriginalUrlForRemoteId(id); + + if (Platform.isAndroid || livePhotoVideoId == null || isVideo) { + tasks[taskIndex++] = DownloadTask( + taskId: id, + url: url, + headers: headers, + filename: asset.name, + updates: Updates.statusAndProgress, + group: isVideo ? downloadGroupVideo : downloadGroupImage, + ); + continue; + } + + _dummyMetadata['part'] = LivePhotosPart.image; + _dummyMetadata['id'] = id; + tasks[taskIndex++] = DownloadTask( + taskId: id, + url: url, + headers: headers, + filename: asset.name, + updates: Updates.statusAndProgress, + group: downloadGroupLivePhoto, + metaData: json.encode(_dummyMetadata), + ); + + _dummyMetadata['part'] = LivePhotosPart.video; + tasks[taskIndex++] = DownloadTask( + taskId: livePhotoVideoId, + url: url, + headers: headers, + filename: asset.name + .toUpperCase() + .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), + updates: Updates.statusAndProgress, + group: downloadGroupLivePhoto, + metaData: json.encode(_dummyMetadata), + ); + } + if (taskIndex == 0) { + return Future.value(const []); + } + return _downloader.enqueueAll(tasks.slice(0, taskIndex)); } } diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index b59df5b3dc..7b0d74e420 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -1,3 +1,5 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -23,6 +25,7 @@ final actionServiceProvider = Provider( ref.watch(driftAlbumApiRepositoryProvider), ref.watch(remoteAlbumRepository), ref.watch(assetMediaRepositoryProvider), + ref.watch(downloadRepositoryProvider), ), ); @@ -33,6 +36,7 @@ class ActionService { final DriftAlbumApiRepository _albumApiRepository; final DriftRemoteAlbumRepository _remoteAlbumRepository; final AssetMediaRepository _assetMediaRepository; + final DownloadRepository _downloadRepository; const ActionService( this._assetApiRepository, @@ -41,6 +45,7 @@ class ActionService { this._albumApiRepository, this._remoteAlbumRepository, this._assetMediaRepository, + this._downloadRepository, ); Future shareLink(List remoteIds, BuildContext context) async { @@ -191,4 +196,8 @@ class ActionService { Future shareAssets(List assets) { return _assetMediaRepository.shareAssets(assets); } + + Future> downloadAll(List assets) { + return _downloadRepository.downloadAllAssets(assets); + } } From dee6d072fb3762ff718562e4de963c9159c5210c Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 20 Jul 2025 13:56:53 -0500 Subject: [PATCH 016/169] fix: memory jarring hero animation (#20030) * fix: memory jarring hero animation * remove other hero --- .../widgets/memory/memory_card.widget.dart | 38 ++++++++----------- .../widgets/memory/memory_lane.widget.dart | 15 +++----- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index a6262bd2e5..268bbc30c0 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -57,30 +57,24 @@ class DriftMemoryCard extends StatelessWidget { } if (asset.isImage) { - return Hero( - tag: 'memory-${asset.id}', - child: FullImage( - asset, - fit: fit, - size: const Size(double.infinity, double.infinity), - ), + return FullImage( + asset, + fit: fit, + size: const Size(double.infinity, double.infinity), ); } else { - return Hero( - tag: 'memory-${asset.id}', - child: SizedBox( - width: context.width, - height: context.height, - child: NativeVideoViewer( - key: ValueKey(asset.id), - asset: asset, - showControls: false, - playbackDelayFactor: 2, - image: FullImage( - asset, - size: Size(context.width, context.height), - fit: BoxFit.contain, - ), + return SizedBox( + width: context.width, + height: context.height, + child: NativeVideoViewer( + key: ValueKey(asset.id), + asset: asset, + showControls: false, + playbackDelayFactor: 2, + image: FullImage( + asset, + size: Size(context.width, context.height), + fit: BoxFit.contain, ), ), ); diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index aa21f36dd1..403d8de061 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -79,15 +79,12 @@ class DriftMemoryCard extends ConsumerWidget { Colors.black.withValues(alpha: 0.2), BlendMode.darken, ), - child: Hero( - tag: 'memory-${memory.assets[0].id}', - child: SizedBox( - width: 205, - height: 200, - child: Thumbnail( - remoteId: memory.assets[0].id, - fit: BoxFit.cover, - ), + child: SizedBox( + width: 205, + height: 200, + child: Thumbnail( + remoteId: memory.assets[0].id, + fit: BoxFit.cover, ), ), ), From 5fc4393e7a2c51584dcd7d2a3d2c7b3ba42ff8eb Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Mon, 21 Jul 2025 23:26:49 +0530 Subject: [PATCH 017/169] feat: migrate backup albums to sqlite (#20049) * do not migrate again on app start * migrate backup albums over to sqlite --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/entities/backup_album.entity.dart | 12 +++ .../pages/common/change_experience.page.dart | 4 + mobile/lib/utils/migration.dart | 85 ++++++++++++++++--- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/mobile/lib/entities/backup_album.entity.dart b/mobile/lib/entities/backup_album.entity.dart index 4d4d7b3aa3..1e96c0452e 100644 --- a/mobile/lib/entities/backup_album.entity.dart +++ b/mobile/lib/entities/backup_album.entity.dart @@ -13,6 +13,18 @@ class BackupAlbum { BackupAlbum(this.id, this.lastBackup, this.selection); Id get isarId => fastHash(id); + + BackupAlbum copyWith({ + String? id, + DateTime? lastBackup, + BackupSelection? selection, + }) { + return BackupAlbum( + id ?? this.id, + lastBackup ?? this.lastBackup, + selection ?? this.selection, + ); + } } enum BackupSelection { diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 5d298edb42..a8569b25a0 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -56,6 +56,10 @@ class _ChangeExperiencePageState extends ConsumerState { ref.read(isarProvider), ref.read(driftProvider), ); + await migrateBackupAlbumsToSqlite( + ref.read(isarProvider), + ref.read(driftProvider), + ); } } else { await ref.read(backgroundSyncProvider).cancel(); diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index d09f2b9978..a95c376ac2 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -2,19 +2,23 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; import 'package:flutter/foundation.dart'; 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'; -import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/android_device_asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/entities/backup_album.entity.dart' + as isar_backup_album; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; @@ -27,7 +31,7 @@ import 'package:logging/logging.dart'; // ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 14; +const int targetVersion = 13; Future migrateDatabaseIfNeeded(Isar db) async { final int version = Store.get(StoreKey.version, targetVersion); @@ -56,18 +60,6 @@ Future migrateDatabaseIfNeeded(Isar db) async { await Store.put(StoreKey.photoManagerCustomFilter, true); } - if (version < 14) { - if (!Store.isBetaTimelineEnabled) { - // Try again when beta timeline is enabled and the app is restarted - return; - } - final backgroundSync = BackgroundSyncManager(); - await backgroundSync.syncLocal(); - final drift = Drift(); - await migrateDeviceAssetToSqlite(db, drift); - await drift.close(); - } - if (targetVersion >= 12) { await Store.put(StoreKey.version, targetVersion); return; @@ -204,6 +196,71 @@ Future migrateDeviceAssetToSqlite(Isar db, Drift drift) async { } } +Future migrateBackupAlbumsToSqlite( + Isar db, + Drift drift, +) async { + try { + final isarBackupAlbums = await db.backupAlbums.where().findAll(); + // Recents is a virtual album on Android, and we don't have it with the new sync + // If recents is selected previously, select all albums during migration except the excluded ones + if (Platform.isAndroid) { + final recentAlbum = + isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); + if (recentAlbum != null) { + await drift.localAlbumEntity.update().write( + const LocalAlbumEntityCompanion( + backupSelection: Value(BackupSelection.selected), + ), + ); + final excluded = isarBackupAlbums + .where( + (album) => + album.selection == isar_backup_album.BackupSelection.exclude, + ) + .map((album) => album.id) + .toList(); + await drift.batch((batch) async { + for (final id in excluded) { + batch.update( + drift.localAlbumEntity, + const LocalAlbumEntityCompanion( + backupSelection: Value(BackupSelection.excluded), + ), + where: (t) => t.id.equals(id), + ); + } + }); + } + return; + } + + await drift.batch((batch) { + for (final album in isarBackupAlbums) { + batch.update( + drift.localAlbumEntity, + LocalAlbumEntityCompanion( + backupSelection: Value( + switch (album.selection) { + isar_backup_album.BackupSelection.none => BackupSelection.none, + isar_backup_album.BackupSelection.select => + BackupSelection.selected, + isar_backup_album.BackupSelection.exclude => + BackupSelection.excluded, + }, + ), + ), + where: (t) => t.id.equals(album.id), + ); + } + }); + } catch (error) { + debugPrint( + "[MIGRATION] Error while migrating backup albums to SQLite: $error", + ); + } +} + class _DeviceAsset { final String assetId; final List? hash; From 1dc62fce5f16425020aa3644672a2b4025c1047e Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 22 Jul 2025 01:50:41 +0530 Subject: [PATCH 018/169] fix: remove foreign constraint on stack.primaryAssetId (#20052) * fix: remove foreign constraint in stack.primaryAssetId * fix migration --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../drift_schemas/main/drift_schema_v3.json | 1 + .../infrastructure/entities/stack.entity.dart | 3 +- .../entities/stack.entity.drift.dart | 135 +- .../repositories/db.repository.dart | 10 +- .../repositories/db.repository.steps.dart | 355 ++ .../repositories/remote_asset.repository.dart | 2 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v3.dart | 5136 +++++++++++++++++ 8 files changed, 5521 insertions(+), 126 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v3.json create mode 100644 mobile/test/drift/main/generated/schema_v3.dart diff --git a/mobile/drift_schemas/main/drift_schema_v3.json b/mobile/drift_schemas/main/drift_schema_v3.json new file mode 100644 index 0000000000..1acfbaf493 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v3.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/infrastructure/entities/stack.entity.dart b/mobile/lib/infrastructure/entities/stack.entity.dart index 92375f19db..b5da42832e 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.dart @@ -1,5 +1,4 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; @@ -15,7 +14,7 @@ class StackEntity extends Table with DriftDefaultsMixin { TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get primaryAssetId => text().references(RemoteAssetEntity, #id)(); + TextColumn get primaryAssetId => text()(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart index c0d000e02a..df0390aea0 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -8,8 +8,6 @@ import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i6; typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion Function({ @@ -57,33 +55,6 @@ final class $$StackEntityTableReferences extends i0.BaseReferences< return i0.ProcessedTableManager( manager.$state.copyWith(prefetchedData: [item])); } - - static i6.$RemoteAssetEntityTable _primaryAssetIdTable( - i0.GeneratedDatabase db) => - i5.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .primaryAssetId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); - - i6.$$RemoteAssetEntityTableProcessedTableManager get primaryAssetId { - final $_column = $_itemColumn('primary_asset_id')!; - - final manager = i6 - .$$RemoteAssetEntityTableTableManager( - $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) - .filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_primaryAssetIdTable($_db)); - if (item == null) return manager; - return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } } class $$StackEntityTableFilterComposer @@ -104,6 +75,10 @@ class $$StackEntityTableFilterComposer i0.ColumnFilters get updatedAt => $composableBuilder( column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, + builder: (column) => i0.ColumnFilters(column)); + i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( composer: this, @@ -125,28 +100,6 @@ class $$StackEntityTableFilterComposer )); return composer; } - - i6.$$RemoteAssetEntityTableFilterComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } } class $$StackEntityTableOrderingComposer @@ -169,6 +122,10 @@ class $$StackEntityTableOrderingComposer column: $table.updatedAt, builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, + builder: (column) => i0.ColumnOrderings(column)); + i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( composer: this, @@ -190,30 +147,6 @@ class $$StackEntityTableOrderingComposer )); return composer; } - - i6.$$RemoteAssetEntityTableOrderingComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableOrderingComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } } class $$StackEntityTableAnnotationComposer @@ -234,6 +167,9 @@ class $$StackEntityTableAnnotationComposer i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); + i0.GeneratedColumn get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, builder: (column) => column); + i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -255,30 +191,6 @@ class $$StackEntityTableAnnotationComposer )); return composer; } - - i6.$$RemoteAssetEntityTableAnnotationComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } } class $$StackEntityTableTableManager extends i0.RootTableManager< @@ -292,7 +204,7 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< $$StackEntityTableUpdateCompanionBuilder, (i1.StackEntityData, i1.$$StackEntityTableReferences), i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})> { + i0.PrefetchHooks Function({bool ownerId})> { $$StackEntityTableTableManager( i0.GeneratedDatabase db, i1.$StackEntityTable table) : super(i0.TableManagerState( @@ -338,7 +250,7 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableReferences(db, table, e) )) .toList(), - prefetchHooksCallback: ({ownerId = false, primaryAssetId = false}) { + prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], @@ -365,17 +277,6 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableReferences._ownerIdTable(db).id, ) as T; } - if (primaryAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.primaryAssetId, - referencedTable: i1.$$StackEntityTableReferences - ._primaryAssetIdTable(db), - referencedColumn: i1.$$StackEntityTableReferences - ._primaryAssetIdTable(db) - .id, - ) as T; - } return state; }, @@ -398,7 +299,7 @@ typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< $$StackEntityTableUpdateCompanionBuilder, (i1.StackEntityData, i1.$$StackEntityTableReferences), i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})>; + i0.PrefetchHooks Function({bool ownerId})>; class $StackEntityTable extends i2.StackEntity with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { @@ -440,12 +341,8 @@ class $StackEntityTable extends i2.StackEntity const i0.VerificationMeta('primaryAssetId'); @override late final i0.GeneratedColumn primaryAssetId = - i0.GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + i0.GeneratedColumn('primary_asset_id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); @override List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 0a763c91cc..7562cf6ff5 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -72,7 +72,7 @@ class Drift extends $Drift implements IDatabaseRepository { ); @override - int get schemaVersion => 2; + int get schemaVersion => 3; @override MigrationStrategy get migration => MigrationStrategy( @@ -84,12 +84,16 @@ class Drift extends $Drift implements IDatabaseRepository { from: from, to: to, steps: migrationSteps( - from1To2: (m, _) async { - for (final entity in allSchemaEntities) { + from1To2: (m, v2) async { + for (final entity in v2.entities) { await m.drop(entity); await m.create(entity); } }, + from2To3: (m, v3) async { + // Removed foreign key constraint on stack.primaryAssetId + await m.alterTable(TableMigration(v3.stackEntity)); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 76e5df76d0..a0703c3714 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -919,8 +919,356 @@ i1.GeneratedColumn _column_73(String aliasedName) => i1.GeneratedColumn _column_74(String aliasedName) => i1.GeneratedColumn('birth_date', aliasedName, true, type: i1.DriftSqlType.dateTime); + +final class Schema3 extends i0.VersionedSchema { + Schema3({required super.database}) : super(version: 3); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); + final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(user_id, "key")', + ], + columns: [ + _column_25, + _column_26, + _column_27, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(shared_by_id, shared_with_id)', + ], + columns: [ + _column_28, + _column_29, + _column_30, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id)', + ], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_36, + _column_60, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(album_id, user_id)', + ], + columns: [ + _column_60, + _column_25, + _column_61, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, memory_id)', + ], + columns: [ + _column_36, + _column_68, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape13 personEntity = Shape13( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null); +} + +i1.GeneratedColumn _column_75(String aliasedName) => + i1.GeneratedColumn('primary_asset_id', aliasedName, false, + type: i1.DriftSqlType.string); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -929,6 +1277,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from1To2(migrator, schema); return 2; + case 2: + final schema = Schema3(database: database); + final migrator = i1.Migrator(database, schema); + await from2To3(migrator, schema); + return 3; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -937,8 +1290,10 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, + from2To3: from2To3, )); diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 28c229b46b..52cfe2e7c2 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -193,7 +193,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity - .filter((row) => row.primaryAssetId.id.isIn(stack.assetIds)) + .filter((row) => row.primaryAssetId.isIn(stack.assetIds)) .map((row) => row.id) .get(); diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index b2b7404b5a..209e70d788 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -5,6 +5,7 @@ import 'package:drift/drift.dart'; import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; +import 'schema_v3.dart' as v3; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -14,10 +15,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v1.DatabaseAtV1(db); case 2: return v2.DatabaseAtV2(db); + case 3: + return v3.DatabaseAtV3(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2]; + static const versions = const [1, 2, 3]; } diff --git a/mobile/test/drift/main/generated/schema_v3.dart b/mobile/test/drift/main/generated/schema_v3.dart new file mode 100644 index 0000000000..8f655b3f7d --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v3.dart @@ -0,0 +1,5136 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn email = GeneratedColumn( + 'email', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + isAdmin: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, + email: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData( + {required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith( + {String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes}) => + UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, + updatedAt, quotaSizeInBytes, quotaUsageInBytes); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith( + {Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes}) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn localDateTime = + GeneratedColumn('local_date_time', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), + thumbHash: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), + visibility: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, + stackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent()}) => + RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: + localDateTime.present ? localDateTime.value : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: + data.visibility.present ? data.visibility.value : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId}) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum']), + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation}) => + LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, + height, durationInSeconds, id, checksum, isFavorite, orientation); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation}) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + @override + List get $columns => + [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + backupSelection: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, + marker_: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData( + {required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith( + {String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent()}) => + LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_}) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn city = GeneratedColumn( + 'city', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn state = GeneratedColumn( + 'state', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn country = GeneratedColumn( + 'country', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn('date_time_original', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn make = GeneratedColumn( + 'make', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn model = GeneratedColumn( + 'model', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + city: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}city']), + state: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}state']), + country: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}country']), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + exposureTime: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), + fNumber: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}f_number']), + fileSize: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}file_size']), + focalLength: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), + latitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}latitude']), + longitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}longitude']), + iso: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}iso']), + make: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}make']), + model: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}model']), + lens: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}lens']), + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}orientation']), + timeZone: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), + rating: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}rating']), + projectionType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData( + {required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: + serializer.fromJson(json['dateTimeOriginal']), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith( + {String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent()}) => + RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: + exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: + projectionType.present ? projectionType.value : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: + data.description.present ? data.description.value : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: + data.focalLength.present ? data.focalLength.value : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith( + {Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType}) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\'')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))'), + defaultValue: const CustomExpression('1')); + late final GeneratedColumn order = GeneratedColumn( + 'order', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, + order: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData( + {required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith( + {String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order}) => + RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: + data.description.present ? data.description.value : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, + ownerId, thumbnailAssetId, isActivityEnabled, order); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order}) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn role = GeneratedColumn( + 'role', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + role: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData( + {required this.albumId, required this.userId, required this.role}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith( + {String? albumId, String? userId, int? role}) => + RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith( + {Value? albumId, Value? userId, Value? role}) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn data = GeneratedColumn( + 'data', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + data: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}data'])!, + isSaved: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, + memoryAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, + seenAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), + showAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), + hideAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent()}) => + MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, + type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt}) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + memoryId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith( + {Value? assetId, Value? memoryId}) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + thumbnailPath: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent()}) => + PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate}) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV3 extends GeneratedDatabase { + DatabaseAtV3(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity + ]; + @override + int get schemaVersion => 3; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} From 4d27f187eaf776a6a3e7ebb08d8309793a09fb4b Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Jul 2025 15:30:51 -0500 Subject: [PATCH 019/169] feat: new upload (cont) (#20029) * new upload button * wip * pr feedback * fix: updateAll override album selection value * feat: status box * feat: handle upload resume * re-enable websocket event * fix: update state condition and upload status * Better backup detail page --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- i18n/en.json | 8 + .../app/alextran/immich/BackupWorker.kt | 1 + mobile/lib/constants/constants.dart | 3 + mobile/lib/domain/models/store.model.dart | 3 +- .../domain/services/local_album.service.dart | 4 +- mobile/lib/domain/utils/background_sync.dart | 16 +- .../extensions/build_context_extensions.dart | 4 + .../repositories/backup.repository.dart | 9 +- .../repositories/local_album.repository.dart | 21 +- mobile/lib/main.dart | 4 +- .../lib/pages/backup/drift_backup.page.dart | 125 ++- .../drift_backup_album_selection.page.dart | 720 ++++++++++++------ .../backup/drift_upload_detail.page.dart | 428 +++++++++++ mobile/lib/pages/common/tab_shell.page.dart | 17 +- .../backup/backup_toggle_button.widget.dart | 242 ++++++ .../providers/app_life_cycle.provider.dart | 24 +- .../backup/backup_album.provider.dart | 4 +- .../backup/drift_backup.provider.dart | 231 +++++- mobile/lib/providers/websocket.provider.dart | 12 +- mobile/lib/routing/router.dart | 5 + mobile/lib/routing/router.gr.dart | 16 + mobile/lib/services/app_settings.service.dart | 1 + mobile/lib/services/drift_backup.service.dart | 15 +- mobile/lib/services/upload.service.dart | 1 + .../widgets/common/immich_sliver_app_bar.dart | 6 + .../settings/beta_timeline_list_tile.dart | 51 +- 26 files changed, 1558 insertions(+), 413 deletions(-) create mode 100644 mobile/lib/pages/backup/drift_upload_detail.page.dart create mode 100644 mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart diff --git a/i18n/en.json b/i18n/en.json index 77dc2e235c..8961943678 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -406,6 +406,7 @@ "album_options": "Album options", "album_remove_user": "Remove user?", "album_remove_user_confirmation": "Are you sure you want to remove {user}?", + "album_search_not_found": "No albums found matching your search", "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.", "album_updated": "Album updated", "album_updated_setting_description": "Receive an email notification when a shared album has new assets", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Default album sort order", "albums_default_sort_order_description": "Initial asset sort order when creating new albums.", "albums_feature_description": "Collections of assets that can be shared with other users.", + "albums_on_device_count": "Albums on device ({count})", "all": "All", "all_albums": "All albums", "all_people": "All people", @@ -605,6 +607,7 @@ "cancel": "Cancel", "cancel_search": "Cancel search", "canceled": "Canceled", + "canceling": "Canceling", "cannot_merge_people": "Cannot merge people", "cannot_undo_this_action": "You cannot undo this action!", "cannot_update_the_description": "Cannot update the description", @@ -765,6 +768,7 @@ "description": "Description", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "deselect_all": "Deselect All", "details": "Details", "direction": "Direction", "disabled": "Disabled", @@ -839,6 +843,7 @@ "empty_trash": "Empty trash", "empty_trash_confirmation": "Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.\nYou cannot undo this action!", "enable": "Enable", + "enable_backup": "Enable Backup", "enable_biometric_auth_description": "Enter your PIN code to enable biometric authentication", "enabled": "Enabled", "end_date": "End date", @@ -1485,6 +1490,7 @@ "purchase_server_description_2": "Supporter status", "purchase_server_title": "Server", "purchase_settings_server_activated": "The server product key is managed by the admin", + "queue_status": "Queuing {count}/{total}", "rating": "Star rating", "rating_clear": "Clear rating", "rating_count": "{count, plural, one {# star} other {# stars}}", @@ -1915,6 +1921,7 @@ "updated_password": "Updated password", "upload": "Upload", "upload_concurrency": "Upload concurrency", + "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", @@ -1965,6 +1972,7 @@ "view_album": "View Album", "view_all": "View All", "view_all_users": "View all users", + "view_details": "View Details", "view_in_timeline": "View in timeline", "view_link": "View link", "view_links": "View links", diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt index 0fb75b002c..9c90528dc9 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt @@ -83,6 +83,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) { runDart() + } return resolvableFuture diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 37a3eec073..206fbbb28f 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -38,3 +38,6 @@ const List<(String, String)> kWidgetNames = [ ('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiver'), ('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiver'), ]; + +const double kUploadStatusFailed = -1.0; +const double kUploadStatusCanceled = -2.0; diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 61c5807ba8..305b3f3387 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -70,7 +70,8 @@ enum StoreKey { // Experimental stuff photoManagerCustomFilter._(1000), betaPromptShown._(1001), - betaTimeline._(1002); + betaTimeline._(1002), + enableBackup._(1003); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 7ec9231196..79cc58f3e0 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -7,8 +7,8 @@ class LocalAlbumService { const LocalAlbumService(this._repository); - Future> getAll() { - return _repository.getAll(); + Future> getAll({Set sortBy = const {}}) { + return _repository.getAll(sortBy: sortBy); } Future getThumbnail(String albumId) { diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 7ab2989354..4a44c4d8f2 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -101,14 +101,18 @@ class BackgroundSyncManager { if (_syncWebsocketTask != null) { return _syncWebsocketTask!.future; } - - _syncWebsocketTask = runInIsolateGentle( - computation: (ref) => ref - .read(syncStreamServiceProvider) - .handleWsAssetUploadReadyV1Batch(batchData), - ); + _syncWebsocketTask = _handleWsAssetUploadReadyV1Batch(batchData); return _syncWebsocketTask!.whenComplete(() { _syncWebsocketTask = null; }); } } + +Cancelable _handleWsAssetUploadReadyV1Batch( + List batchData, +) => + runInIsolateGentle( + computation: (ref) => ref + .read(syncStreamServiceProvider) + .handleWsAssetUploadReadyV1Batch(batchData), + ); diff --git a/mobile/lib/extensions/build_context_extensions.dart b/mobile/lib/extensions/build_context_extensions.dart index 69a9c3b347..7bb194cdae 100644 --- a/mobile/lib/extensions/build_context_extensions.dart +++ b/mobile/lib/extensions/build_context_extensions.dart @@ -33,6 +33,10 @@ extension ContextHelper on BuildContext { // Returns the current Primary color of the Theme Color get primaryColor => themeData.colorScheme.primary; + Color get logoYellow => const Color.fromARGB(255, 255, 184, 0); + Color get logoRed => const Color.fromARGB(255, 230, 65, 30); + Color get logoPink => const Color.fromARGB(255, 222, 127, 179); + Color get logoGreen => const Color.fromARGB(255, 49, 164, 82); // Returns the Scaffold background color of the Theme Color get scaffoldBackgroundColor => colorScheme.surface; diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 4ce3b07e8b..99df206db5 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -52,9 +52,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { Future getRemainderCount() async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns( - [_db.localAlbumAssetEntity.assetId], - ) + ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ innerJoin( _db.localAlbumEntity, @@ -147,6 +145,11 @@ class DriftBackupRepository extends DriftDatabaseRepository { ), ) & lae.id.isNotInQuery(_getExcludedSubquery()), + ) + ..orderBy( + [ + (localAsset) => OrderingTerm.desc(localAsset.createdAt), + ], ); return query.map((localAsset) => localAsset.toDto()).get(); diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index ba9dfd979d..5f192a20cf 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -8,7 +8,13 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/utils/database.utils.dart'; import 'package:platform/platform.dart'; -enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum } +enum SortLocalAlbumsBy { + id, + backupSelection, + isIosSharedAlbum, + name, + assetCount +} class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; @@ -41,6 +47,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { OrderingTerm.asc(_db.localAlbumEntity.backupSelection), SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), + SortLocalAlbumsBy.name => + OrderingTerm.asc(_db.localAlbumEntity.name), + SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), }, ); } @@ -151,7 +160,15 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insert( _db.localAlbumEntity, companion, - onConflict: DoUpdate((_) => companion), + onConflict: DoUpdate( + (old) => LocalAlbumEntityCompanion( + id: companion.id, + name: companion.name, + updatedAt: companion.updatedAt, + isIosSharedAlbum: companion.isIosSharedAlbum, + marker_: companion.marker_, + ), + ), ); } }); diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index acadf4c887..f036fd9bc3 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -96,8 +96,8 @@ Future initApp() async { // Initialize the file downloader await FileDownloader().configure( - // maxConcurrent: 5, maxConcurrentByHost: 2, maxConcurrentByGroup: 3 - globalConfig: (Config.holdingQueue, (5, 2, 3)), + // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 + globalConfig: (Config.holdingQueue, (6, 6, 3)), ); await FileDownloader().trackTasksInGroup( diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 34649ca42c..1b9ec8ad07 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -1,83 +1,61 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; -import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; @RoutePage() -class DriftBackupPage extends HookConsumerWidget { +class DriftBackupPage extends ConsumerStatefulWidget { const DriftBackupPage({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { - useEffect( - () { - ref.read(driftBackupProvider.notifier).getBackupStatus(); - return null; - }, - [], - ); + ConsumerState createState() => _DriftBackupPageState(); +} - Widget buildControlButtons() { - return Padding( - padding: const EdgeInsets.only( - top: 24, - ), - child: Column( - children: [ - ElevatedButton( - onPressed: () => ref.read(driftBackupProvider.notifier).backup(), - child: const Text( - "backup_controller_page_start_backup", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ).tr(), - ), - OutlinedButton( - onPressed: () => ref.read(driftBackupProvider.notifier).cancel(), - child: const Text( - "cancel", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ).tr(), - ), - OutlinedButton( - onPressed: () => - ref.read(driftBackupProvider.notifier).getDataInfo(), - child: const Text( - "Get database info", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ).tr(), - ), - ], - ), - ); - } +class _DriftBackupPageState extends ConsumerState { + @override + void initState() { + super.initState(); + ref.read(driftBackupProvider.notifier).getBackupStatus(); + } + + Future startBackup() async { + await ref.read(driftBackupProvider.notifier).getBackupStatus(); + await ref.read(driftBackupProvider.notifier).backup(); + } + + Future stopBackup() async { + await ref.read(driftBackupProvider.notifier).cancel(); + } + + @override + Widget build(BuildContext context) { + final selectedAlbum = ref + .watch(backupAlbumProvider) + .where( + (album) => album.backupSelection == BackupSelection.selected, + ) + .toList(); + final uploadItems = ref.watch( + driftBackupProvider.select((state) => state.uploadItems), + ); return Scaffold( appBar: AppBar( elevation: 0, - title: const Text( - "Backup (Experimental)", + title: Text( + "backup_controller_page_backup".t(), ), leading: IconButton( onPressed: () { - ref.watch(websocketProvider.notifier).listenUploadEvent(); context.maybePop(true); }, splashRadius: 24, @@ -85,18 +63,6 @@ class DriftBackupPage extends HookConsumerWidget { Icons.arrow_back_ios_rounded, ), ), - actions: [ - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: IconButton( - onPressed: () => context.pushRoute(const BackupOptionsRoute()), - splashRadius: 24, - icon: const Icon( - Icons.settings_outlined, - ), - ), - ), - ], ), body: Stack( children: [ @@ -110,11 +76,24 @@ class DriftBackupPage extends HookConsumerWidget { children: [ const SizedBox(height: 8), const _BackupAlbumSelectionCard(), - const _TotalCard(), - const _BackupCard(), - const _RemainderCard(), - const Divider(), - buildControlButtons(), + if (selectedAlbum.isNotEmpty) ...[ + const _TotalCard(), + const _BackupCard(), + const _RemainderCard(), + const Divider(), + BackupToggleButton( + onStart: () async => await startBackup(), + onStop: () async => await stopBackup(), + ), + if (uploadItems.isNotEmpty) + TextButton.icon( + icon: const Icon(Icons.info_outline_rounded), + onPressed: () => context.pushRoute( + const DriftUploadDetailRoute(), + ), + label: Text("view_details".t(context: context)), + ), + ], ], ), ), diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 2e7a2d4c2d..fd39f0a579 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -1,25 +1,67 @@ +import 'dart:io'; + import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; @RoutePage() -class DriftBackupAlbumSelectionPage extends HookConsumerWidget { +class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { const DriftBackupAlbumSelectionPage({super.key}); + @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => + _DriftBackupAlbumSelectionPageState(); +} + +class _DriftBackupAlbumSelectionPageState + extends ConsumerState { + String _searchQuery = ''; + bool _isSearchMode = false; + late ValueNotifier _enableSyncUploadAlbum; + late TextEditingController _searchController; + late FocusNode _searchFocusNode; + + @override + void initState() { + super.initState(); + _enableSyncUploadAlbum = ValueNotifier(false); + _searchController = TextEditingController(); + _searchFocusNode = FocusNode(); + + _enableSyncUploadAlbum.value = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.syncAlbums); + ref.read(backupAlbumProvider.notifier).getAll(); + } + + @override + void dispose() { + _enableSyncUploadAlbum.dispose(); + _searchController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { final albums = ref.watch(backupAlbumProvider); + final albumCount = albums.length; + // Filter albums based on search query + final filteredAlbums = albums.where((album) { + if (_searchQuery.isEmpty) return true; + return album.name.toLowerCase().contains(_searchQuery.toLowerCase()); + }).toList(); final selectedBackupAlbums = albums .where((album) => album.backupSelection == BackupSelection.selected) @@ -27,133 +69,6 @@ class DriftBackupAlbumSelectionPage extends HookConsumerWidget { final excludedBackupAlbums = albums .where((album) => album.backupSelection == BackupSelection.excluded) .toList(); - final enableSyncUploadAlbum = - useAppSettingsState(AppSettingsEnum.syncAlbums); - final isDarkTheme = context.isDarkTheme; - - useEffect( - () { - ref.watch(backupProvider.notifier).getBackupInfo(); - ref.watch(backupAlbumProvider.notifier).getAll(); - return null; - }, - [], - ); - - buildAlbumSelectionList() { - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return DriftAlbumInfoListTile( - album: albums[index], - ); - }), - childCount: albums.length, - ), - ), - ); - } - - buildAlbumSelectionGrid() { - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.all(12.0), - sliver: SliverGrid.builder( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 300, - mainAxisSpacing: 12, - crossAxisSpacing: 12, - ), - itemCount: albums.length, - itemBuilder: ((context, index) { - return DriftAlbumInfoListTile( - album: albums[index], - ); - }), - ), - ); - } - - buildSelectedAlbumNameChip() { - return selectedBackupAlbums.map((album) { - void removeSelection() { - ref.read(backupAlbumProvider.notifier).deselectAlbum(album); - } - - return Padding( - padding: const EdgeInsets.only(right: 8.0), - child: GestureDetector( - onTap: removeSelection, - child: Chip( - label: Text( - album.name, - style: TextStyle( - fontSize: 12, - color: isDarkTheme ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), - ), - backgroundColor: context.primaryColor, - deleteIconColor: isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), - onDeleted: removeSelection, - ), - ), - ); - }).toSet(); - } - - buildExcludedAlbumNameChip() { - return excludedBackupAlbums.map((album) { - void removeSelection() { - ref.read(backupAlbumProvider.notifier).deselectAlbum(album); - } - - return GestureDetector( - onTap: removeSelection, - child: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Chip( - label: Text( - album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), - ), - backgroundColor: Colors.red[300], - deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), - onDeleted: removeSelection, - ), - ), - ); - }).toSet(); - } handleSyncAlbumToggle(bool isEnable) async { if (isEnable) { @@ -170,138 +85,439 @@ class DriftBackupAlbumSelectionPage extends HookConsumerWidget { onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded), ), - title: const Text( - "backup_album_selection_page_select_albums", - ).tr(), + title: _isSearchMode + ? SearchField( + hintText: 'search_albums'.t(context: context), + autofocus: true, + controller: _searchController, + focusNode: _searchFocusNode, + onChanged: (value) => + setState(() => _searchQuery = value.trim()), + ) + : const Text( + "backup_album_selection_page_select_albums", + ).t(context: context), + actions: [ + if (!_isSearchMode) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() { + _isSearchMode = true; + _searchQuery = ''; + }), + ) + else + IconButton( + icon: const Icon(Icons.close), + onPressed: () => setState(() { + _isSearchMode = false; + _searchQuery = ''; + _searchController.clear(); + }), + ), + ], elevation: 0, ), - body: SafeArea( - child: CustomScrollView( - physics: const ClampingScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).tr(), + body: CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, ), - // Selected Album Chips + child: Text( + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, + ).t(context: context), + ), + // Selected Album Chips - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - ...buildSelectedAlbumNameChip(), - ...buildExcludedAlbumNameChip(), - ], - ), - ), - - SettingsSwitchListTile( - valueNotifier: enableSyncUploadAlbum, - title: "sync_albums".tr(), - subtitle: "sync_upload_album_setting_subtitle".tr(), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), - onChanged: handleSyncAlbumToggle, - ), - - ListTile( - title: Text( - "backup_album_selection_page_albums_device".tr( - namedArgs: { - 'count': ref - .watch(backupProvider) - .availableAlbums - .length - .toString(), - }, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + _SelectedAlbumNameChips( + selectedBackupAlbums: selectedBackupAlbums, ), - style: context.textTheme.titleSmall, + _ExcludedAlbumNameChips( + excludedBackupAlbums: excludedBackupAlbums, + ), + ], + ), + ), + + SettingsSwitchListTile( + valueNotifier: _enableSyncUploadAlbum, + title: "sync_albums".t(context: context), + subtitle: + "sync_upload_album_setting_subtitle".t(context: context), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.primary, + ), + onChanged: handleSyncAlbumToggle, + ), + + ListTile( + title: Text( + "albums_on_device_count".t( + context: context, + args: {'count': albumCount.toString()}, ), - subtitle: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), - ).tr(), - ), - trailing: IconButton( - splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith( color: context.primaryColor, ), - onPressed: () { - // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(10)), - ), - elevation: 5, - title: Text( - 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), - ).tr(), - content: SingleChildScrollView( - child: ListBody( - children: [ - const Text( - 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), - ).tr(), - ], - ), - ), - ); - }, - ); - }, - ), + ).t(context: context), ), + trailing: IconButton( + splashRadius: 16, + icon: Icon( + Icons.info, + size: 20, + color: context.primaryColor, + ), + onPressed: () { + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(10)), + ), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: context.primaryColor, + ), + ).t(context: context), + content: SingleChildScrollView( + child: ListBody( + children: [ + const Text( + 'backup_album_selection_page_assets_scatter', + style: TextStyle( + fontSize: 14, + ), + ).t(context: context), + ], + ), + ), + ); + }, + ); + }, + ), + ), - // buildSearchBar(), - ], - ), + if (Platform.isAndroid) + _SelectAllButton( + filteredAlbums: filteredAlbums, + selectedBackupAlbums: selectedBackupAlbums, + ), + ], ), - SliverLayoutBuilder( - builder: (context, constraints) { - if (constraints.crossAxisExtent > 600) { - return buildAlbumSelectionGrid(); - } else { - return buildAlbumSelectionList(); - } - }, - ), - ], + ), + SliverLayoutBuilder( + builder: (context, constraints) { + if (constraints.crossAxisExtent > 600) { + return _AlbumSelectionGrid( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } else { + return _AlbumSelectionList( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } + }, + ), + ], + ), + ); + } +} + +class _AlbumSelectionList extends StatelessWidget { + final List filteredAlbums; + final String searchQuery; + + const _AlbumSelectionList({ + required this.filteredAlbums, + required this.searchQuery, + }); + + @override + Widget build(BuildContext context) { + if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Text('album_search_not_found'.t(context: context)), + ), + ), + ); + } + + if (filteredAlbums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + ((context, index) { + return DriftAlbumInfoListTile( + album: filteredAlbums[index], + ); + }), + childCount: filteredAlbums.length, ), ), ); } } + +class _AlbumSelectionGrid extends StatelessWidget { + final List filteredAlbums; + final String searchQuery; + + const _AlbumSelectionGrid({ + required this.filteredAlbums, + required this.searchQuery, + }); + + @override + Widget build(BuildContext context) { + if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Text('album_search_not_found'.t(context: context)), + ), + ), + ); + } + + if (filteredAlbums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.all(12.0), + sliver: SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + ), + itemCount: filteredAlbums.length, + itemBuilder: ((context, index) { + return DriftAlbumInfoListTile( + album: filteredAlbums[index], + ); + }), + ), + ); + } +} + +class _SelectedAlbumNameChips extends ConsumerWidget { + final List selectedBackupAlbums; + + const _SelectedAlbumNameChips({ + required this.selectedBackupAlbums, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Wrap( + children: selectedBackupAlbums.asMap().entries.map((entry) { + final album = entry.value; + + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: GestureDetector( + onTap: removeSelection, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: Chip( + label: Text( + album.name, + style: TextStyle( + fontSize: 12, + color: context.isDarkTheme ? Colors.black : Colors.white, + fontWeight: FontWeight.bold, + ), + ), + backgroundColor: context.primaryColor, + deleteIconColor: + context.isDarkTheme ? Colors.black : Colors.white, + deleteIcon: const Icon( + Icons.cancel_rounded, + size: 15, + ), + onDeleted: removeSelection, + ), + ), + ), + ); + }).toList(), + ); + } +} + +class _ExcludedAlbumNameChips extends ConsumerWidget { + final List excludedBackupAlbums; + + const _ExcludedAlbumNameChips({ + required this.excludedBackupAlbums, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Wrap( + children: excludedBackupAlbums.asMap().entries.map((entry) { + final album = entry.value; + + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return GestureDetector( + onTap: removeSelection, + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: Chip( + label: Text( + album.name, + style: TextStyle( + fontSize: 12, + color: context.scaffoldBackgroundColor, + fontWeight: FontWeight.bold, + ), + ), + backgroundColor: Colors.red[300], + deleteIconColor: context.scaffoldBackgroundColor, + deleteIcon: const Icon( + Icons.cancel_rounded, + size: 15, + ), + onDeleted: removeSelection, + ), + ), + ), + ); + }).toList(), + ); + } +} + +class _SelectAllButton extends ConsumerWidget { + final List filteredAlbums; + final List selectedBackupAlbums; + + const _SelectAllButton({ + required this.filteredAlbums, + required this.selectedBackupAlbums, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final canSelectAll = filteredAlbums + .where((album) => album.backupSelection != BackupSelection.selected) + .isNotEmpty; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + children: [ + Expanded( + child: ElevatedButton.icon( + onPressed: canSelectAll + ? () { + for (final album in filteredAlbums) { + if (album.backupSelection != BackupSelection.selected) { + ref + .read(backupAlbumProvider.notifier) + .selectAlbum(album); + } + } + } + : null, + icon: const Icon(Icons.select_all), + label: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: Text( + "select_all".t(context: context), + ), + ), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12.0), + ), + ), + ), + const SizedBox(width: 8.0), + Expanded( + child: OutlinedButton.icon( + onPressed: selectedBackupAlbums.isNotEmpty + ? () { + for (final album in filteredAlbums) { + if (album.backupSelection == BackupSelection.selected) { + ref + .read(backupAlbumProvider.notifier) + .deselectAlbum(album); + } + } + } + : null, + icon: const Icon(Icons.deselect), + label: Text('deselect_all'.t(context: context)), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12.0), + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart new file mode 100644 index 0000000000..66803265e6 --- /dev/null +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -0,0 +1,428 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:path/path.dart' as path; + +@RoutePage() +class DriftUploadDetailPage extends ConsumerWidget { + const DriftUploadDetailPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final uploadItems = ref.watch( + driftBackupProvider.select((state) => state.uploadItems), + ); + + return Scaffold( + appBar: AppBar( + title: Text("upload_details".t(context: context)), + backgroundColor: context.colorScheme.surface, + elevation: 0, + scrolledUnderElevation: 1, + ), + body: uploadItems.isEmpty + ? _buildEmptyState(context) + : _buildUploadList(uploadItems), + ); + } + + Widget _buildEmptyState(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.cloud_upload_outlined, + size: 80, + color: context.colorScheme.onSurface.withValues(alpha: 0.3), + ), + const SizedBox(height: 16), + Text( + "no_uploads_in_progress".t(context: context), + style: context.textTheme.titleMedium?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.6), + ), + ), + ], + ), + ); + } + + Widget _buildUploadList( + Map uploadItems, + ) { + return ListView.separated( + addAutomaticKeepAlives: true, + padding: const EdgeInsets.all(16), + itemCount: uploadItems.length, + separatorBuilder: (context, index) => const SizedBox(height: 4), + itemBuilder: (context, index) { + final item = uploadItems.values.elementAt(index); + return _buildUploadCard(context, item); + }, + ); + } + + Widget _buildUploadCard( + BuildContext context, + DriftUploadStatus item, + ) { + final isCompleted = item.progress >= 1.0; + final double progressPercentage = (item.progress * 100).clamp(0, 100); + + return Card( + elevation: 0, + color: context.colorScheme.surfaceContainer, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + side: BorderSide( + color: context.colorScheme.outline.withValues(alpha: 0.1), + width: 1, + ), + ), + child: InkWell( + onTap: () => _showFileDetailDialog(context, item), + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + path.basename(item.filename), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + 'Tap for more details', + style: context.textTheme.bodySmall?.copyWith( + color: context.colorScheme.onSurface + .withValues(alpha: 0.6), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + _buildProgressIndicator( + context, + item.progress, + progressPercentage, + isCompleted, + item.networkSpeedAsString, + ), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _buildProgressIndicator( + BuildContext context, + double progress, + double percentage, + bool isCompleted, + String networkSpeedAsString, + ) { + return Column( + children: [ + Stack( + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 36, + height: 36, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 300), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: + context.colorScheme.outline.withValues(alpha: 0.2), + strokeWidth: 3, + value: value, + color: isCompleted + ? context.colorScheme.primary + : context.colorScheme.secondary, + ), + ), + ), + if (isCompleted) + Icon( + Icons.check_circle_rounded, + size: 28, + color: context.colorScheme.primary, + ) + else + Text( + percentage.toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.bold, + fontSize: 10, + ), + ), + ], + ), + Text( + networkSpeedAsString, + style: context.textTheme.labelSmall?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.6), + fontSize: 10, + ), + ), + ], + ); + } + + Future _showFileDetailDialog( + BuildContext context, + DriftUploadStatus item, + ) async { + showDialog( + context: context, + builder: (context) => FileDetailDialog(uploadStatus: item), + ); + } +} + +class FileDetailDialog extends ConsumerWidget { + final DriftUploadStatus uploadStatus; + + const FileDetailDialog({ + super.key, + required this.uploadStatus, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AlertDialog( + insetPadding: const EdgeInsets.all(20), + backgroundColor: context.colorScheme.surfaceContainerLow, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + side: BorderSide( + color: context.colorScheme.outline.withValues(alpha: 0.2), + width: 1, + ), + ), + title: Row( + children: [ + Icon( + Icons.info_outline, + color: context.primaryColor, + size: 24, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + "details".t(context: context), + style: context.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + ), + ], + ), + content: SizedBox( + width: double.maxFinite, + child: FutureBuilder( + future: _getAssetDetails(ref, uploadStatus.taskId), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const SizedBox( + height: 200, + child: Center(child: CircularProgressIndicator()), + ); + } + + final asset = snapshot.data; + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // Thumbnail at the top center + Center( + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Container( + width: 128, + height: 128, + decoration: BoxDecoration( + border: Border.all( + color: context.colorScheme.outline + .withValues(alpha: 0.2), + width: 1, + ), + borderRadius: + const BorderRadius.all(Radius.circular(12)), + ), + child: asset != null + ? Thumbnail( + asset: asset, + size: const Size(512, 512), + fit: BoxFit.cover, + ) + : null, + ), + ), + ), + const SizedBox(height: 24), + if (asset != null) ...[ + _buildInfoSection(context, [ + _buildInfoRow( + context, + "Filename", + path.basename(uploadStatus.filename), + ), + _buildInfoRow( + context, + "Local ID", + asset.id, + ), + _buildInfoRow( + context, + "File Size", + formatHumanReadableBytes(uploadStatus.fileSize, 2), + ), + if (asset.width != null) + _buildInfoRow(context, "Width", "${asset.width}px"), + if (asset.height != null) + _buildInfoRow( + context, + "Height", + "${asset.height}px", + ), + _buildInfoRow( + context, + "Created At", + asset.createdAt.toString(), + ), + _buildInfoRow( + context, + "Updated At", + asset.updatedAt.toString(), + ), + if (asset.checksum != null) + _buildInfoRow( + context, + "Checksum", + asset.checksum!, + ), + ]), + ], + ], + ), + ); + }, + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + "close".t(), + style: TextStyle( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + ), + ], + ); + } + + Widget _buildInfoSection( + BuildContext context, + List children, + ) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all( + Radius.circular(12), + ), + border: Border.all( + color: context.colorScheme.outline.withValues(alpha: 0.1), + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...children, + ], + ), + ); + } + + Widget _buildInfoRow(BuildContext context, String label, String value) { + return Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 100, + child: Text( + "$label:", + style: context.textTheme.labelMedium?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + ), + ), + ), + Expanded( + child: Text( + value, + style: context.textTheme.labelMedium?.copyWith(), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + Future _getAssetDetails( + WidgetRef ref, + String localAssetId, + ) async { + try { + final repository = ref.read(localAssetRepository); + return await repository.getById(localAssetId); + } catch (e) { + return null; + } + } +} diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 671c1a6156..b0be136a15 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -3,7 +3,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; @@ -11,6 +13,7 @@ import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/migration.dart'; @RoutePage() @@ -25,9 +28,19 @@ class _TabShellPageState extends ConsumerState { @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { + + WidgetsBinding.instance.addPostFrameCallback((_) async { ref.read(websocketProvider.notifier).connect(); - runNewSync(ref, full: true); + + final isEnableBackup = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableBackup); + + await runNewSync(ref, full: true).then((_) async { + if (isEnableBackup) { + await ref.read(driftBackupProvider.notifier).handleBackupResume(); + } + }); }); } diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart new file mode 100644 index 0000000000..c817b9b4b6 --- /dev/null +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; + +class BackupToggleButton extends ConsumerStatefulWidget { + final VoidCallback onStart; + final VoidCallback onStop; + + const BackupToggleButton({ + super.key, + required this.onStart, + required this.onStop, + }); + + @override + ConsumerState createState() => BackupToggleButtonState(); +} + +class BackupToggleButtonState extends ConsumerState + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _gradientAnimation; + bool _isEnabled = false; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(seconds: 8), + vsync: this, + ); + + _gradientAnimation = Tween(begin: 0, end: 1).animate( + CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOut, + ), + ); + + _isEnabled = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableBackup); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + Future _onToggle(bool value) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.enableBackup, value); + + setState(() { + _isEnabled = value; + }); + + if (value) { + widget.onStart.call(); + } else { + widget.onStop.call(); + } + } + + @override + Widget build(BuildContext context) { + final enqueueCount = ref.watch( + driftBackupProvider.select((state) => state.enqueueCount), + ); + + final enqueueTotalCount = ref.watch( + driftBackupProvider.select((state) => state.enqueueTotalCount), + ); + + final isCanceling = ref.watch( + driftBackupProvider.select((state) => state.isCanceling), + ); + + final uploadTasks = ref.watch( + driftBackupProvider.select((state) => state.uploadItems), + ); + + final isUploading = uploadTasks.isNotEmpty; + + return AnimatedBuilder( + animation: _animationController, + builder: (context, child) { + final gradientColors = [ + Color.lerp( + context.primaryColor.withValues(alpha: 0.5), + context.primaryColor.withValues(alpha: 0.3), + _gradientAnimation.value, + )!, + Color.lerp( + context.primaryColor.withValues(alpha: 0.2), + context.primaryColor.withValues(alpha: 0.4), + _gradientAnimation.value, + )!, + Color.lerp( + context.primaryColor.withValues(alpha: 0.3), + context.primaryColor.withValues(alpha: 0.5), + _gradientAnimation.value, + )!, + ]; + + return Container( + margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + gradient: LinearGradient( + colors: gradientColors, + stops: const [0.0, 0.5, 1.0], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow( + color: context.primaryColor.withValues(alpha: 0.1), + blurRadius: 12, + offset: const Offset(0, 2), + ), + ], + ), + child: Container( + margin: const EdgeInsets.all(1.5), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(18.5)), + color: context.colorScheme.surfaceContainerLow, + ), + child: Material( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(20.5)), + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(20.5)), + onTap: () => isCanceling ? null : _onToggle(!_isEnabled), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + context.primaryColor.withValues(alpha: 0.2), + context.primaryColor.withValues(alpha: 0.1), + ], + ), + ), + child: isUploading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ) + : Icon( + Icons.cloud_upload_outlined, + color: context.primaryColor, + size: 24, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "enable_backup".t(context: context), + style: + context.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + ], + ), + if (enqueueCount != enqueueTotalCount) + Text( + "queue_status".t( + context: context, + args: { + 'count': enqueueCount.toString(), + 'total': enqueueTotalCount.toString(), + }, + ), + style: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), + if (isCanceling) + Row( + children: [ + Text( + "canceling".t(), + style: context.textTheme.labelLarge, + ), + const SizedBox(width: 4), + SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + backgroundColor: context + .colorScheme.onSurface + .withValues(alpha: 0.2), + ), + ), + ], + ), + ], + ), + ), + Switch.adaptive( + value: _isEnabled, + onChanged: (value) => + isCanceling ? null : _onToggle(value), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 5984160241..3be46d2fbd 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -6,10 +6,12 @@ import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; @@ -18,6 +20,7 @@ import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; @@ -69,25 +72,18 @@ class AppLifeCycleNotifier extends StateNotifier { } await _ref.read(serverInfoProvider.notifier).getServerVersion(); - - // TODO: Need to decide on how we want to handle uploads once the app is resumed - // await FileDownloader().start(); } if (!Store.isBetaTimelineEnabled) { switch (_ref.read(tabProvider)) { case TabEnum.home: await _ref.read(assetProvider.notifier).getAllAsset(); - break; - case TabEnum.search: - // nothing to do - break; case TabEnum.albums: await _ref.read(albumProvider.notifier).refreshRemoteAlbums(); - break; + case TabEnum.library: - // nothing to do + case TabEnum.search: break; } } else { @@ -108,7 +104,15 @@ class AppLifeCycleNotifier extends StateNotifier { }, ), backgroundManager.syncRemote(), - ]); + ]).then((_) async { + final isEnableBackup = _ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableBackup); + + if (isEnableBackup) { + await _ref.read(driftBackupProvider.notifier).handleBackupResume(); + } + }); } catch (e, stackTrace) { Logger("AppLifeCycleNotifier").severe( "Error during background sync", diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart index b36d3ac57e..2915c7c216 100644 --- a/mobile/lib/providers/backup/backup_album.provider.dart +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -1,6 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/services/local_album.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; final backupAlbumProvider = @@ -18,7 +19,8 @@ class BackupAlbumNotifier extends StateNotifier> { final LocalAlbumService _localAlbumService; Future getAll() async { - state = await _localAlbumService.getAll(); + state = + await _localAlbumService.getAll(sortBy: {SortLocalAlbumsBy.assetCount}); } Future selectAlbum(LocalAlbum album) async { diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 2544d208c4..c51c40775e 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -1,39 +1,75 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:async'; +import 'dart:convert'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; + import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/services/drift_backup.service.dart'; import 'package:immich_mobile/services/upload.service.dart'; +class EnqueueStatus { + final int enqueueCount; + final int totalCount; + + const EnqueueStatus({ + required this.enqueueCount, + required this.totalCount, + }); + + EnqueueStatus copyWith({ + int? enqueueCount, + int? totalCount, + }) { + return EnqueueStatus( + enqueueCount: enqueueCount ?? this.enqueueCount, + totalCount: totalCount ?? this.totalCount, + ); + } + + @override + String toString() => + 'EnqueueStatus(enqueueCount: $enqueueCount, totalCount: $totalCount)'; +} + class DriftUploadStatus { final String taskId; final String filename; final double progress; + final int fileSize; + final String networkSpeedAsString; const DriftUploadStatus({ required this.taskId, required this.filename, required this.progress, + required this.fileSize, + required this.networkSpeedAsString, }); DriftUploadStatus copyWith({ String? taskId, String? filename, double? progress, + int? fileSize, + String? networkSpeedAsString, }) { return DriftUploadStatus( taskId: taskId ?? this.taskId, filename: filename ?? this.filename, progress: progress ?? this.progress, + fileSize: fileSize ?? this.fileSize, + networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, ); } @override - String toString() => - 'ExpUploadStatus(taskId: $taskId, filename: $filename, progress: $progress)'; + String toString() { + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString)'; + } @override bool operator ==(covariant DriftUploadStatus other) { @@ -41,23 +77,65 @@ class DriftUploadStatus { return other.taskId == taskId && other.filename == filename && - other.progress == progress; + other.progress == progress && + other.fileSize == fileSize && + other.networkSpeedAsString == networkSpeedAsString; } @override - int get hashCode => taskId.hashCode ^ filename.hashCode ^ progress.hashCode; + int get hashCode { + return taskId.hashCode ^ + filename.hashCode ^ + progress.hashCode ^ + fileSize.hashCode ^ + networkSpeedAsString.hashCode; + } + + Map toMap() { + return { + 'taskId': taskId, + 'filename': filename, + 'progress': progress, + 'fileSize': fileSize, + 'networkSpeedAsString': networkSpeedAsString, + }; + } + + factory DriftUploadStatus.fromMap(Map map) { + return DriftUploadStatus( + taskId: map['taskId'] as String, + filename: map['filename'] as String, + progress: map['progress'] as double, + fileSize: map['fileSize'] as int, + networkSpeedAsString: map['networkSpeedAsString'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory DriftUploadStatus.fromJson(String source) => + DriftUploadStatus.fromMap(json.decode(source) as Map); } class DriftBackupState { final int totalCount; final int backupCount; final int remainderCount; + + final int enqueueCount; + final int enqueueTotalCount; + + final bool isCanceling; + final Map uploadItems; const DriftBackupState({ required this.totalCount, required this.backupCount, required this.remainderCount, + required this.enqueueCount, + required this.enqueueTotalCount, + required this.isCanceling, required this.uploadItems, }); @@ -65,19 +143,25 @@ class DriftBackupState { int? totalCount, int? backupCount, int? remainderCount, + int? enqueueCount, + int? enqueueTotalCount, + bool? isCanceling, Map? uploadItems, }) { return DriftBackupState( totalCount: totalCount ?? this.totalCount, backupCount: backupCount ?? this.backupCount, remainderCount: remainderCount ?? this.remainderCount, + enqueueCount: enqueueCount ?? this.enqueueCount, + enqueueTotalCount: enqueueTotalCount ?? this.enqueueTotalCount, + isCanceling: isCanceling ?? this.isCanceling, uploadItems: uploadItems ?? this.uploadItems, ); } @override String toString() { - return 'ExpBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, uploadItems: $uploadItems)'; + return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, uploadItems: $uploadItems)'; } @override @@ -88,6 +172,9 @@ class DriftBackupState { return other.totalCount == totalCount && other.backupCount == backupCount && other.remainderCount == remainderCount && + other.enqueueCount == enqueueCount && + other.enqueueTotalCount == enqueueTotalCount && + other.isCanceling == isCanceling && mapEquals(other.uploadItems, uploadItems); } @@ -96,6 +183,9 @@ class DriftBackupState { return totalCount.hashCode ^ backupCount.hashCode ^ remainderCount.hashCode ^ + enqueueCount.hashCode ^ + enqueueTotalCount.hashCode ^ + isCanceling.hashCode ^ uploadItems.hashCode; } } @@ -117,6 +207,9 @@ class ExpBackupNotifier extends StateNotifier { totalCount: 0, backupCount: 0, remainderCount: 0, + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: false, uploadItems: {}, ), ) { @@ -131,13 +224,39 @@ class ExpBackupNotifier extends StateNotifier { StreamSubscription? _statusSubscription; StreamSubscription? _progressSubscription; + /// Remove upload item from state + void _removeUploadItem(String taskId) { + if (state.uploadItems.containsKey(taskId)) { + final updatedItems = + Map.from(state.uploadItems); + updatedItems.remove(taskId); + state = state.copyWith(uploadItems: updatedItems); + } + } + void _handleTaskStatusUpdate(TaskStatusUpdate update) { switch (update.status) { case TaskStatus.complete: - state = state.copyWith( - backupCount: state.backupCount + 1, - remainderCount: state.remainderCount - 1, - ); + if (update.task.group == kBackupGroup) { + state = state.copyWith( + backupCount: state.backupCount + 1, + remainderCount: state.remainderCount - 1, + ); + } + + // Remove the completed task from the upload items + final taskId = update.task.taskId; + if (state.uploadItems.containsKey(taskId)) { + Future.delayed(const Duration(milliseconds: 500), () { + _removeUploadItem(taskId); + }); + } + + case TaskStatus.failed: + break; + + case TaskStatus.canceled: + _removeUploadItem(update.task.taskId); break; default: @@ -145,7 +264,48 @@ class ExpBackupNotifier extends StateNotifier { } } - void _handleTaskProgressUpdate(TaskProgressUpdate update) {} + void _handleTaskProgressUpdate(TaskProgressUpdate update) { + final taskId = update.task.taskId; + final filename = update.task.displayName; + final progress = update.progress; + final currentItem = state.uploadItems[taskId]; + if (currentItem != null) { + if (progress == kUploadStatusCanceled) { + _removeUploadItem(update.task.taskId); + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: update.hasExpectedFileSize + ? currentItem.copyWith( + progress: progress, + fileSize: update.expectedFileSize, + networkSpeedAsString: update.networkSpeedAsString, + ) + : currentItem.copyWith( + progress: progress, + ), + }, + ); + + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: DriftUploadStatus( + taskId: taskId, + filename: filename, + progress: progress, + fileSize: update.expectedFileSize, + networkSpeedAsString: update.networkSpeedAsString, + ), + }, + ); + } Future getBackupStatus() async { final [totalCount, backupCount, remainderCount] = await Future.wait([ @@ -162,27 +322,50 @@ class ExpBackupNotifier extends StateNotifier { } Future backup() { - return _backupService.backup(); + return _backupService.backup(_updateEnqueueCount); + } + + void _updateEnqueueCount(EnqueueStatus status) { + state = state.copyWith( + enqueueCount: status.enqueueCount, + enqueueTotalCount: status.totalCount, + ); } Future cancel() async { + state = state.copyWith( + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: true, + ); + await _backupService.cancel(); - await getDataInfo(); + + // Check if there are any tasks left in the queue + final tasks = await FileDownloader().allTasks(group: kBackupGroup); + + debugPrint("Tasks left to cancel: ${tasks.length}"); + + if (tasks.isNotEmpty) { + await cancel(); + } else { + // Clear all upload items when cancellation is complete + state = state.copyWith( + isCanceling: false, + uploadItems: {}, + ); + } } - Future getDataInfo() async { - final a = await FileDownloader().database.allRecordsWithStatus( - TaskStatus.enqueued, - group: kBackupGroup, - ); + Future handleBackupResume() async { + final tasks = await FileDownloader().allTasks(group: kBackupGroup); + if (tasks.isEmpty) { + // Start a new backup queue + await backup(); + } - final b = await FileDownloader().allTasks( - group: kBackupGroup, - ); - - debugPrint( - "Enqueued tasks: ${a.length}, All tasks: ${b.length}", - ); + debugPrint("Tasks to resume: ${tasks.length}"); + await FileDownloader().start(); } @override diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index 05fe5a087c..2718738286 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +// import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -177,9 +178,15 @@ class WebsocketNotifier extends StateNotifier { }); if (!Store.isBetaTimelineEnabled) { - startListeningToOldEvents(); + socket.on('on_upload_success', _handleOnUploadSuccess); + socket.on('on_asset_delete', _handleOnAssetDelete); + socket.on('on_asset_trash', _handleOnAssetTrash); + socket.on('on_asset_restore', _handleServerUpdates); + socket.on('on_asset_update', _handleServerUpdates); + socket.on('on_asset_stack_update', _handleServerUpdates); + socket.on('on_asset_hidden', _handleOnAssetHidden); } else { - startListeningToBetaEvents(); + socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); } socket.on('on_config_update', _handleOnConfigUpdate); @@ -207,7 +214,6 @@ class WebsocketNotifier extends StateNotifier { } void stopListenToEvent(String eventName) { - debugPrint("Stop listening to event $eventName"); state.socket?.off(eventName); } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index d7dd45dbd9..d0f8852dc3 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -27,6 +27,7 @@ import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; import 'package:immich_mobile/pages/common/app_log.page.dart'; @@ -484,6 +485,10 @@ class AppRouter extends RootStackRouter { page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: DriftUploadDetailRoute.page, + guards: [_authGuard, _duplicateGuard], + ), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index c72dc62765..c9ed8a40a3 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -1078,6 +1078,22 @@ class DriftTrashRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftUploadDetailPage] +class DriftUploadDetailRoute extends PageRouteInfo { + const DriftUploadDetailRoute({List? children}) + : super(DriftUploadDetailRoute.name, initialChildren: children); + + static const String name = 'DriftUploadDetailRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftUploadDetailPage(); + }, + ); +} + /// generated route for /// [DriftUserSelectionPage] class DriftUserSelectionRoute diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 982dab1cae..cefc52385a 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -91,6 +91,7 @@ enum AppSettingsEnum { true, ), betaTimeline(StoreKey.betaTimeline, null, false), + enableBackup(StoreKey.enableBackup, null, false), ; const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 5966aad304..2f51c261fb 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/services/upload.service.dart'; @@ -55,7 +56,9 @@ class DriftBackupService { return _backupRepository.getBackupCount(); } - Future backup() async { + Future backup( + void Function(EnqueueStatus status) onEnqueueTasks, + ) async { shouldCancel = false; final candidates = await _backupRepository.getCandidates(); @@ -83,8 +86,12 @@ class DriftBackupService { if (tasks.isNotEmpty && !shouldCancel) { count += tasks.length; _uploadService.enqueueTasks(tasks); - debugPrint( - "Enqueued $count/${candidates.length} tasks for backup", + + onEnqueueTasks( + EnqueueStatus( + enqueueCount: count, + totalCount: candidates.length, + ), ); } } @@ -213,7 +220,7 @@ class DriftBackupService { deviceAssetId: asset.id, fields: fields, group: kBackupLivePhotoGroup, - priority: 0, + priority: 0, // Highest priority to get upload immediately ); } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 60aab4a16c..b869624e52 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -134,6 +134,7 @@ class UploadService { group: group, priority: priority ?? 5, updates: Updates.statusAndProgress, + retries: 3, ); } } diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 2b020d20ee..c7ddeca6e0 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -83,6 +84,11 @@ class ImmichSliverAppBar extends ConsumerWidget { child: action, ), ), + if (kDebugMode || kProfileMode) + IconButton( + icon: const Icon(Icons.science_rounded), + onPressed: () => context.pushRoute(const FeatInDevRoute()), + ), if (showUploadButton) const Padding( padding: EdgeInsets.only(right: 20), diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 1de3a6e7ab..a9c873cb67 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -90,6 +90,19 @@ class _BetaTimelineListTileState extends ConsumerState ), actions: [ TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + "cancel".t(context: context), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: context.colorScheme.outline, + ), + ), + ), + ElevatedButton( onPressed: () async { Navigator.of(context).pop(); await ref.read(appSettingsServiceProvider).setSetting( @@ -101,25 +114,7 @@ class _BetaTimelineListTileState extends ConsumerState ); }, child: Text( - "YES", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.primaryColor, - ), - ), - ), - TextButton( - onPressed: () { - context.pop(); - }, - child: Text( - "NO", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.colorScheme.outline, - ), + "ok".t(context: context), ), ), ], @@ -135,13 +130,13 @@ class _BetaTimelineListTileState extends ConsumerState _gradientAnimation.value, )!, Color.lerp( - context.primaryColor.withValues(alpha: 0.4), - context.primaryColor.withValues(alpha: 0.6), + context.logoPink.withValues(alpha: 0.2), + context.logoPink.withValues(alpha: 0.4), _gradientAnimation.value, )!, Color.lerp( - context.primaryColor.withValues(alpha: 0.3), - context.primaryColor.withValues(alpha: 0.5), + context.logoRed.withValues(alpha: 0.3), + context.logoRed.withValues(alpha: 0.5), _gradientAnimation.value, )!, ]; @@ -155,7 +150,7 @@ class _BetaTimelineListTileState extends ConsumerState stops: const [0.0, 0.5, 1.0], begin: Alignment.topLeft, end: Alignment.bottomRight, - transform: GradientRotation(_rotationAnimation.value * 0.1), + transform: GradientRotation(_rotationAnimation.value * 0.5), ), boxShadow: [ BoxShadow( @@ -166,13 +161,12 @@ class _BetaTimelineListTileState extends ConsumerState ], ), child: Container( - margin: const EdgeInsets.all(1.5), + margin: const EdgeInsets.all(2), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10.5)), color: context.scaffoldBackgroundColor, ), child: Material( - color: Colors.transparent, borderRadius: const BorderRadius.all(Radius.circular(10.5)), child: InkWell( borderRadius: const BorderRadius.all(Radius.circular(10.5)), @@ -205,7 +199,7 @@ class _BetaTimelineListTileState extends ConsumerState ), ), ), - const SizedBox(width: 16), + const SizedBox(width: 28), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -259,8 +253,9 @@ class _BetaTimelineListTileState extends ConsumerState .t(context: context), style: context.textTheme.labelLarge?.copyWith( color: context.textTheme.labelLarge?.color - ?.withValues(alpha: 0.7), + ?.withValues(alpha: 0.9), ), + maxLines: 2, ), ], ), From 7ea878359367f9076fd64fe28d8b9f12d350b84e Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 22 Jul 2025 03:18:22 +0530 Subject: [PATCH 020/169] chore: do not render drift migration auto gen files (#20060) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitattributes b/.gitattributes index 3d43ff20ed..e3fb061bbc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,6 +12,12 @@ mobile/lib/**/*.drift.dart linguist-generated=true mobile/drift_schemas/main/drift_schema_*.json -diff -merge mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true +mobile/lib/infrastructure/repositories/db.repository.steps.dart -diff -merge +mobile/lib/infrastructure/repositories/db.repository.steps.dart linguist-generated=true + +mobile/test/drift/main/generated/** -diff -merge +mobile/test/drift/main/generated/** linguist-generated=true + open-api/typescript-sdk/fetch-client.ts -diff -merge open-api/typescript-sdk/fetch-client.ts linguist-generated=true From 99e5b33969bcb172f26ca72406d4e081987c3ec8 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 21 Jul 2025 14:51:16 -0700 Subject: [PATCH 021/169] feat: add favorites support to android widget (#20061) --- .../app/alextran/immich/widget/ImageDownloadWorker.kt | 11 +++++++---- .../immich/widget/configure/RandomConfigure.kt | 2 +- .../kotlin/app/alextran/immich/widget/model/Model.kt | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt index 2a23d0ecbd..3915f291f8 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -169,13 +169,16 @@ class ImageDownloadWorker( ): WidgetEntry { val api = ImmichAPI(serverConfig) - val filters = SearchFilters(AssetType.IMAGE) + val filters = SearchFilters() val albumId = widgetConfig[kSelectedAlbum] val showSubtitle = widgetConfig[kShowAlbumName] val albumName = widgetConfig[kSelectedAlbumName] var subtitle: String? = if (showSubtitle == true) albumName else "" - if (albumId != null) { + + if (albumId == "FAVORITES") { + filters.isFavorite = true + } else if (albumId != null) { filters.albumIds = listOf(albumId) } @@ -183,7 +186,7 @@ class ImageDownloadWorker( // handle an empty album, fallback to random if (randomSearch.isEmpty() && albumId != null) { - randomSearch = api.fetchSearchResults(SearchFilters(AssetType.IMAGE)) + randomSearch = api.fetchSearchResults(SearchFilters()) subtitle = "" } @@ -215,7 +218,7 @@ class ImageDownloadWorker( val yearDiff = today.year - memory.data.year subtitle = "$yearDiff ${if (yearDiff == 1) "year" else "years"} ago" } else { - val filters = SearchFilters(AssetType.IMAGE, size=1) + val filters = SearchFilters(size=1) asset = api.fetchSearchResults(filters).first() } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt index d0490c023e..83e404a8f1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt @@ -98,7 +98,7 @@ fun RandomConfiguration(context: Context, appWidgetId: Int, glanceId: GlanceId, albumItems = listOf(DropdownItem(currentAlbumName, currentAlbumId)) } - availableAlbums = listOf(DropdownItem("None", "NONE")) + albumItems + availableAlbums = listOf(DropdownItem("None", "NONE"), DropdownItem("Favorites", "FAVORITES")) + albumItems // load selected configuration val albumEntity = availableAlbums.firstOrNull { it.id == currentAlbumId } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt index 2337de0612..9595a3b696 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt @@ -17,7 +17,8 @@ data class Asset( data class SearchFilters( var type: AssetType = AssetType.IMAGE, val size: Int = 1, - var albumIds: List = listOf() + var albumIds: List = listOf(), + var isFavorite: Boolean? = null ) data class MemoryResult( From 737e768212d1bb758b74ada600a83a9a922f7f1c Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Jul 2025 16:58:53 -0500 Subject: [PATCH 022/169] feat: drift partners (#20051) * feat: drift toggle partner in timeline * partners operation * fix: lint --- mobile/lib/domain/models/user.model.dart | 88 ++++++++++ .../lib/domain/services/partner.service.dart | 61 +++++++ .../repositories/partner.repository.dart | 164 ++++++++++++++++++ .../library/partner/drift_partner.page.dart | 161 +++++++++++++++++ .../pages/drift_library.page.dart | 16 +- .../pages/drift_partner_detail.page.dart | 64 +++++-- .../widgets/partner_user_avatar.widget.dart | 32 ++++ .../infrastructure/partner.provider.dart | 92 ++++++++++ .../infrastructure/user.provider.dart | 22 +++ mobile/lib/routing/router.dart | 6 + mobile/lib/routing/router.gr.dart | 20 ++- 11 files changed, 703 insertions(+), 23 deletions(-) create mode 100644 mobile/lib/domain/services/partner.service.dart create mode 100644 mobile/lib/infrastructure/repositories/partner.repository.dart create mode 100644 mobile/lib/pages/library/partner/drift_partner.page.dart create mode 100644 mobile/lib/presentation/widgets/partner_user_avatar.widget.dart create mode 100644 mobile/lib/providers/infrastructure/partner.provider.dart diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index abf2e5620b..6cafd8d149 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + import 'package:immich_mobile/domain/models/user_metadata.model.dart'; // TODO: Rename to User once Isar is removed @@ -123,3 +126,88 @@ quotaSizeInBytes: $quotaSizeInBytes, quotaUsageInBytes.hashCode ^ quotaSizeInBytes.hashCode; } + +class PartnerUserDto { + final String id; + final String email; + final String name; + final bool inTimeline; + + final String? profileImagePath; + + const PartnerUserDto({ + required this.id, + required this.email, + required this.name, + required this.inTimeline, + this.profileImagePath, + }); + + PartnerUserDto copyWith({ + String? id, + String? email, + String? name, + bool? inTimeline, + String? profileImagePath, + }) { + return PartnerUserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + inTimeline: inTimeline ?? this.inTimeline, + profileImagePath: profileImagePath ?? this.profileImagePath, + ); + } + + Map toMap() { + return { + 'id': id, + 'email': email, + 'name': name, + 'inTimeline': inTimeline, + 'profileImagePath': profileImagePath, + }; + } + + factory PartnerUserDto.fromMap(Map map) { + return PartnerUserDto( + id: map['id'] as String, + email: map['email'] as String, + name: map['name'] as String, + inTimeline: map['inTimeline'] as bool, + profileImagePath: map['profileImagePath'] != null + ? map['profileImagePath'] as String + : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory PartnerUserDto.fromJson(String source) => + PartnerUserDto.fromMap(json.decode(source) as Map); + + @override + String toString() { + return 'PartnerUserDto(id: $id, email: $email, name: $name, inTimeline: $inTimeline, profileImagePath: $profileImagePath)'; + } + + @override + bool operator ==(covariant PartnerUserDto other) { + if (identical(this, other)) return true; + + return other.id == id && + other.email == email && + other.name == name && + other.inTimeline == inTimeline && + other.profileImagePath == profileImagePath; + } + + @override + int get hashCode { + return id.hashCode ^ + email.hashCode ^ + name.hashCode ^ + inTimeline.hashCode ^ + profileImagePath.hashCode; + } +} diff --git a/mobile/lib/domain/services/partner.service.dart b/mobile/lib/domain/services/partner.service.dart new file mode 100644 index 0000000000..065560c4be --- /dev/null +++ b/mobile/lib/domain/services/partner.service.dart @@ -0,0 +1,61 @@ +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; + +class DriftPartnerService { + final DriftPartnerRepository _driftPartnerRepository; + final PartnerApiRepository _partnerApiRepository; + + const DriftPartnerService( + this._driftPartnerRepository, + this._partnerApiRepository, + ); + + Future> getSharedWith(String userId) { + return _driftPartnerRepository.getSharedWith(userId); + } + + Future> getSharedBy(String userId) { + return _driftPartnerRepository.getSharedBy(userId); + } + + Future> getAvailablePartners( + String currentUserId, + ) async { + final otherUsers = + await _driftPartnerRepository.getAvailablePartners(currentUserId); + final currentPartners = + await _driftPartnerRepository.getSharedBy(currentUserId); + final available = otherUsers.where((user) { + return !currentPartners.any((partner) => partner.id == user.id); + }).toList(); + + return available; + } + + Future toggleShowInTimeline(String partnerId, String userId) async { + final partner = await _driftPartnerRepository.getPartner(partnerId, userId); + if (partner == null) { + debugPrint("Partner not found: $partnerId for user: $userId"); + return; + } + + await _partnerApiRepository.update( + partnerId, + inTimeline: !partner.inTimeline, + ); + + await _driftPartnerRepository.toggleShowInTimeline(partner, userId); + } + + Future addPartner(String partnerId, String userId) async { + await _partnerApiRepository.create(partnerId); + await _driftPartnerRepository.create(partnerId, userId); + } + + Future removePartner(String partnerId, String userId) async { + await _partnerApiRepository.delete(partnerId); + await _driftPartnerRepository.delete(partnerId, userId); + } +} diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart new file mode 100644 index 0000000000..b3b057b035 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -0,0 +1,164 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPartnerRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPartnerRepository(this._db) : super(_db); + + Future> getPartners(String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), + ), + ]) + ..where( + _db.partnerEntity.sharedWithId.equals(userId), + ); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto( + id: user.id, + email: user.email, + name: user.name, + inTimeline: partner.inTimeline, + ); + }).get(); + } + + // Get users who we can share our library with + Future> getAvailablePartners(String currentUserId) { + final query = _db.select(_db.userEntity) + ..where((row) => row.id.equals(currentUserId).not()); + + return query.map((user) { + return PartnerUserDto( + id: user.id, + email: user.email, + name: user.name, + inTimeline: false, + ); + }).get(); + } + + // Get users who are sharing their photos WITH the current user + Future> getSharedWith(String partnerId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), + ), + ]) + ..where( + _db.partnerEntity.sharedWithId.equals(partnerId), + ); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto( + id: user.id, + email: user.email, + name: user.name, + inTimeline: partner.inTimeline, + ); + }).get(); + } + + // Get users who the current user is sharing their photos TO + Future> getSharedBy(String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId), + ), + ]) + ..where( + _db.partnerEntity.sharedById.equals(userId), + ); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto( + id: user.id, + email: user.email, + name: user.name, + inTimeline: partner.inTimeline, + ); + }).get(); + } + + Future> getAllPartnerIds(String userId) async { + // Get users who are sharing with me (sharedWithId = userId) + final sharingWithMeQuery = _db.select(_db.partnerEntity) + ..where((tbl) => tbl.sharedWithId.equals(userId)); + final sharingWithMe = + await sharingWithMeQuery.map((row) => row.sharedById).get(); + + // Get users who I am sharing with (sharedById = userId) + final sharingWithThemQuery = _db.select(_db.partnerEntity) + ..where((tbl) => tbl.sharedById.equals(userId)); + final sharingWithThem = + await sharingWithThemQuery.map((row) => row.sharedWithId).get(); + + // Combine both lists and remove duplicates + final allPartnerIds = + {...sharingWithMe, ...sharingWithThem}.toList(); + return allPartnerIds; + } + + Future getPartner(String partnerId, String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), + ), + ]) + ..where( + _db.partnerEntity.sharedById.equals(partnerId) & + _db.partnerEntity.sharedWithId.equals(userId), + ); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto( + id: user.id, + email: user.email, + name: user.name, + inTimeline: partner.inTimeline, + ); + }).getSingleOrNull(); + } + + Future toggleShowInTimeline(PartnerUserDto partner, String userId) { + return _db.partnerEntity.update().replace( + PartnerEntityCompanion( + sharedById: Value(partner.id), + sharedWithId: Value(userId), + inTimeline: Value(!partner.inTimeline), + ), + ); + } + + Future create(String partnerId, String userId) { + final entity = PartnerEntityCompanion( + sharedById: Value(userId), + sharedWithId: Value(partnerId), + inTimeline: const Value(false), + ); + + return _db.partnerEntity.insertOne(entity); + } + + Future delete(String partnerId, String userId) { + return _db.partnerEntity.deleteWhere( + (t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId), + ); + } +} diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart new file mode 100644 index 0000000000..04efbe066c --- /dev/null +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -0,0 +1,161 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +@RoutePage() +class DriftPartnerPage extends HookConsumerWidget { + const DriftPartnerPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final potentialPartnersAsync = ref.watch(driftAvailablePartnerProvider); + + addNewUsersHandler() async { + final potentialPartners = potentialPartnersAsync.value; + if (potentialPartners == null || potentialPartners.isEmpty) { + ImmichToast.show( + context: context, + msg: "partner_page_no_more_users".tr(), + ); + return; + } + + final selectedUser = await showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: const Text("partner_page_select_partner").tr(), + children: [ + for (PartnerUserDto partner in potentialPartners) + SimpleDialogOption( + onPressed: () => context.pop(partner), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: PartnerUserAvatar(partner: partner), + ), + Text(partner.name), + ], + ), + ), + ], + ); + }, + ); + if (selectedUser != null) { + await ref.read(partnerUsersProvider.notifier).addPartner(selectedUser); + } + } + + onDeleteUser(PartnerUserDto partner) { + return showDialog( + context: context, + builder: (BuildContext context) { + return ConfirmDialog( + title: "stop_photo_sharing", + content: "partner_page_stop_sharing_content" + .tr(namedArgs: {'partner': partner.name}), + onOk: () => + ref.read(partnerUsersProvider.notifier).removePartner(partner), + ); + }, + ); + } + + return Scaffold( + appBar: AppBar( + title: const Text("partners").t(context: context), + elevation: 0, + centerTitle: false, + actions: [ + IconButton( + onPressed: potentialPartnersAsync.whenOrNull( + data: (data) => addNewUsersHandler, + ), + icon: const Icon(Icons.person_add), + tooltip: "add_partner".tr(), + ), + ], + ), + body: _SharedToPartnerList( + onAddPartner: addNewUsersHandler, + onDeletePartner: onDeleteUser, + ), + ); + } +} + +class _SharedToPartnerList extends ConsumerWidget { + final VoidCallback onAddPartner; + final Function(PartnerUserDto partner) onDeletePartner; + + const _SharedToPartnerList({ + required this.onAddPartner, + required this.onDeletePartner, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final partnerAsync = ref.watch(driftSharedByPartnerProvider); + + return partnerAsync.when( + data: (partners) { + if (partners.isEmpty) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: const Text( + "partner_page_empty_message", + style: TextStyle(fontSize: 14), + ).tr(), + ), + Align( + alignment: Alignment.center, + child: ElevatedButton.icon( + onPressed: onAddPartner, + icon: const Icon(Icons.person_add), + label: const Text("add_partner").tr(), + ), + ), + ], + ), + ); + } + + return ListView.builder( + itemCount: partners.length, + itemBuilder: (context, index) { + final partner = partners[index]; + return ListTile( + leading: PartnerUserAvatar(partner: partner), + title: Text(partner.name), + subtitle: Text(partner.email), + trailing: IconButton( + icon: const Icon(Icons.person_remove), + onPressed: () => onDeletePartner(partner), + ), + ); + }, + ); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stack) => Center( + child: Text("Error loading partners: $error"), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 552733980e..eba0a5ea81 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -6,15 +6,15 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; +import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -import 'package:immich_mobile/providers/partner.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/search/people.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; -import 'package:immich_mobile/widgets/common/user_avatar.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; @@ -391,7 +391,8 @@ class _QuickAccessButtonList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final partners = ref.watch(partnerSharedWithProvider); + final partnerSharedWithAsync = ref.watch(driftSharedWithPartnerProvider); + final partners = partnerSharedWithAsync.valueOrNull ?? []; return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32), @@ -452,7 +453,6 @@ class _QuickAccessButtonList extends ConsumerWidget { fontWeight: FontWeight.w500, ), ), - // TODO: PIN code is needed onTap: () => context.pushRoute(const DriftLockedFolderRoute()), ), ListTile( @@ -466,7 +466,7 @@ class _QuickAccessButtonList extends ConsumerWidget { fontWeight: FontWeight.w500, ), ), - onTap: () => context.pushRoute(const PartnerRoute()), + onTap: () => context.pushRoute(const DriftPartnerRoute()), ), _PartnerList(partners: partners), ], @@ -480,7 +480,7 @@ class _QuickAccessButtonList extends ConsumerWidget { class _PartnerList extends StatelessWidget { const _PartnerList({required this.partners}); - final List partners; + final List partners; @override Widget build(BuildContext context) { @@ -503,7 +503,9 @@ class _PartnerList extends StatelessWidget { left: 12.0, right: 18.0, ), - leading: userAvatar(context, partner, radius: 16), + leading: PartnerUserAvatar( + partner: partner, + ), title: const Text( "partner_list_user_photos", style: TextStyle( diff --git a/mobile/lib/presentation/pages/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/drift_partner_detail.page.dart index baae893d39..6c77a480ea 100644 --- a/mobile/lib/presentation/pages/drift_partner_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_partner_detail.page.dart @@ -6,11 +6,14 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftPartnerDetailPage extends StatelessWidget { - final UserDto partner; + final PartnerUserDto partner; const DriftPartnerDetailPage({ super.key, @@ -35,12 +38,7 @@ class DriftPartnerDetailPage extends StatelessWidget { title: partner.name, icon: Icons.person_outline, ), - topSliverWidget: _InfoBox( - onTap: () => { - // TODO: Create DriftUserProvider/DriftUserService to handle this action - }, - inTimeline: partner.inTimeline, - ), + topSliverWidget: _InfoBox(partner: partner), topSliverWidgetHeight: 110, bottomSheet: const PartnerDetailBottomSheet(), ), @@ -48,15 +46,53 @@ class DriftPartnerDetailPage extends StatelessWidget { } } -class _InfoBox extends StatelessWidget { - final VoidCallback onTap; - final bool inTimeline; +class _InfoBox extends ConsumerStatefulWidget { + final PartnerUserDto partner; const _InfoBox({ - required this.onTap, - required this.inTimeline, + required this.partner, }); + @override + ConsumerState<_InfoBox> createState() => _InfoBoxState(); +} + +class _InfoBoxState extends ConsumerState<_InfoBox> { + bool _inTimeline = false; + + @override + void initState() { + super.initState(); + _inTimeline = widget.partner.inTimeline; + } + + _toggleInTimeline() async { + final user = ref.read(currentUserProvider); + if (user == null) { + return; + } + + try { + await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline( + widget.partner.id, + user.id, + ); + + setState(() { + _inTimeline = !_inTimeline; + }); + } catch (error, stack) { + debugPrint("Failed to toggle in timeline: $error $stack"); + ImmichToast.show( + context: context, + toastType: ToastType.error, + durationInSecond: 1, + msg: "Failed to toggle the timeline setting", + ); + return; + } + } + @override Widget build(BuildContext context) { return SliverToBoxAdapter( @@ -96,8 +132,8 @@ class _InfoBox extends StatelessWidget { style: context.textTheme.bodyMedium, ), trailing: Switch( - value: inTimeline, - onChanged: (_) => onTap(), + value: _inTimeline, + onChanged: (_) => _toggleInTimeline(), ), ), ), diff --git a/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart b/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart new file mode 100644 index 0000000000..9be55cae67 --- /dev/null +++ b/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart @@ -0,0 +1,32 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/services/api.service.dart'; + +class PartnerUserAvatar extends StatelessWidget { + const PartnerUserAvatar({super.key, required this.partner}); + + final PartnerUserDto partner; + + @override + Widget build(BuildContext context) { + final url = + "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image"; + final nameFirstLetter = partner.name.isNotEmpty ? partner.name[0] : ""; + return CircleAvatar( + radius: 16, + backgroundColor: context.primaryColor.withAlpha(50), + foregroundImage: CachedNetworkImageProvider( + url, + headers: ApiService.getRequestHeaders(), + cacheKey: "user-${partner.id}-profile", + ), + // silence errors if user has no profile image, use initials as fallback + onForegroundImageError: (exception, stackTrace) {}, + child: Text(nameFirstLetter.toUpperCase()), + ); + } +} diff --git a/mobile/lib/providers/infrastructure/partner.provider.dart b/mobile/lib/providers/infrastructure/partner.provider.dart new file mode 100644 index 0000000000..e1c7ebf960 --- /dev/null +++ b/mobile/lib/providers/infrastructure/partner.provider.dart @@ -0,0 +1,92 @@ +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/partner.service.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class PartnerNotifier extends Notifier> { + late DriftPartnerService _driftPartnerService; + + @override + List build() { + _driftPartnerService = ref.read(driftPartnerServiceProvider); + return []; + } + + Future _loadPartners() async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + state = await _driftPartnerService.getSharedWith(currentUser.id); + } + + Future> getPartners(String userId) async { + final partners = await _driftPartnerService.getSharedWith(userId); + state = partners; + return partners; + } + + Future toggleShowInTimeline(String partnerId, String userId) async { + await _driftPartnerService.toggleShowInTimeline(partnerId, userId); + await _loadPartners(); + } + + Future addPartner(PartnerUserDto partner) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _driftPartnerService.addPartner(partner.id, currentUser.id); + await _loadPartners(); + ref.invalidate(driftAvailablePartnerProvider); + ref.invalidate(driftSharedByPartnerProvider); + } + + Future removePartner(PartnerUserDto partner) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _driftPartnerService.removePartner(partner.id, currentUser.id); + await _loadPartners(); + ref.invalidate(driftAvailablePartnerProvider); + ref.invalidate(driftSharedByPartnerProvider); + } +} + +final driftAvailablePartnerProvider = + FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref + .watch(driftPartnerServiceProvider) + .getAvailablePartners(currentUser.id); +}); + +final driftSharedByPartnerProvider = + FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref.watch(driftPartnerServiceProvider).getSharedBy(currentUser.id); +}); + +final driftSharedWithPartnerProvider = + FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref.watch(driftPartnerServiceProvider).getSharedWith(currentUser.id); +}); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index ca65f8be14..d328f97600 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -1,10 +1,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/partner.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'user.provider.g.dart'; @@ -23,3 +28,20 @@ UserService userService(Ref ref) => UserService( userApiRepository: ref.watch(userApiRepositoryProvider), storeService: ref.watch(storeServiceProvider), ); + +/// Drifts +final driftPartnerRepositoryProvider = Provider( + (ref) => DriftPartnerRepository(ref.watch(driftProvider)), +); + +final driftPartnerServiceProvider = Provider( + (ref) => DriftPartnerService( + ref.watch(driftPartnerRepositoryProvider), + ref.watch(partnerApiRepositoryProvider), + ), +); + +final partnerUsersProvider = + NotifierProvider>( + PartnerNotifier.new, +); diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index d0f8852dc3..ba31ccef2b 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -51,6 +51,7 @@ import 'package:immich_mobile/pages/library/library.page.dart'; import 'package:immich_mobile/pages/library/local_albums.page.dart'; import 'package:immich_mobile/pages/library/locked/locked.page.dart'; import 'package:immich_mobile/pages/library/locked/pin_auth.page.dart'; +import 'package:immich_mobile/pages/library/partner/drift_partner.page.dart'; import 'package:immich_mobile/pages/library/partner/partner.page.dart'; import 'package:immich_mobile/pages/library/partner/partner_detail.page.dart'; import 'package:immich_mobile/pages/library/people/people_collection.page.dart'; @@ -485,6 +486,11 @@ class AppRouter extends RootStackRouter { page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard], ), + + AutoRoute( + page: DriftPartnerRoute.page, + guards: [_authGuard, _duplicateGuard], + ), AutoRoute( page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard], diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index c9ed8a40a3..0e24f776d8 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -896,7 +896,7 @@ class DriftPartnerDetailRoute extends PageRouteInfo { DriftPartnerDetailRoute({ Key? key, - required UserDto partner, + required PartnerUserDto partner, List? children, }) : super( DriftPartnerDetailRoute.name, @@ -920,7 +920,7 @@ class DriftPartnerDetailRouteArgs { final Key? key; - final UserDto partner; + final PartnerUserDto partner; @override String toString() { @@ -928,6 +928,22 @@ class DriftPartnerDetailRouteArgs { } } +/// generated route for +/// [DriftPartnerPage] +class DriftPartnerRoute extends PageRouteInfo { + const DriftPartnerRoute({List? children}) + : super(DriftPartnerRoute.name, initialChildren: children); + + static const String name = 'DriftPartnerRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPartnerPage(); + }, + ); +} + /// generated route for /// [DriftPlaceDetailPage] class DriftPlaceDetailRoute extends PageRouteInfo { From 826eaedae64d4ef10e27bd2ef3458db2ea88b4d2 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Mon, 21 Jul 2025 23:59:42 +0200 Subject: [PATCH 023/169] feat: play live photos on hover (#19962) --- web/src/lib/components/assets/thumbnail/thumbnail.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index 04f6d94e0f..e07e5e99c6 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -343,11 +343,12 @@

{/if} From df318ac641fce158af944cecb1dcf1e1fd9192f4 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 22 Jul 2025 02:31:45 +0100 Subject: [PATCH 024/169] feat: asset face sync (#20048) * chore: remove thumbnailPath from person sync dto * feat: asset face sync --- .../repositories/sync_stream.repository.dart | 1 - mobile/openapi/README.md | 2 + mobile/openapi/lib/api.dart | 2 + mobile/openapi/lib/api_client.dart | 4 + .../lib/model/sync_asset_face_delete_v1.dart | 99 ++++++++++ .../openapi/lib/model/sync_asset_face_v1.dart | 175 ++++++++++++++++++ .../openapi/lib/model/sync_entity_type.dart | 6 + mobile/openapi/lib/model/sync_person_v1.dart | 10 +- .../openapi/lib/model/sync_request_type.dart | 3 + open-api/immich-openapi-specs.json | 66 ++++++- open-api/typescript-sdk/src/fetch-client.ts | 3 + server/src/database.ts | 2 + server/src/dtos/sync.dto.ts | 22 ++- server/src/enum.ts | 4 + server/src/queries/sync.repository.sql | 36 +++- server/src/repositories/sync.repository.ts | 49 ++++- server/src/schema/functions.ts | 13 ++ server/src/schema/index.ts | 5 + ...04909784-AssetFaceUpdateIdAndAuditTable.ts | 52 ++++++ .../schema/tables/asset-face-audit.table.ts | 17 ++ server/src/schema/tables/asset-face.table.ts | 17 ++ server/src/services/sync.service.ts | 16 ++ server/test/fixtures/face.stub.ts | 16 ++ server/test/medium.factory.ts | 6 + .../medium/specs/sync/sync-asset-face.spec.ts | 92 +++++++++ .../medium/specs/sync/sync-person.spec.ts | 1 - 26 files changed, 699 insertions(+), 20 deletions(-) create mode 100644 mobile/openapi/lib/model/sync_asset_face_delete_v1.dart create mode 100644 mobile/openapi/lib/model/sync_asset_face_v1.dart create mode 100644 server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts create mode 100644 server/src/schema/tables/asset-face-audit.table.ts create mode 100644 server/test/medium/specs/sync/sync-asset-face.spec.ts diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index fb5c7fdb3c..e141c387be 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -523,7 +523,6 @@ class SyncStreamRepository extends DriftDatabaseRepository { ownerId: Value(person.ownerId), name: Value(person.name), faceAssetId: Value(person.faceAssetId), - thumbnailPath: Value(person.thumbnailPath), isFavorite: Value(person.isFavorite), isHidden: Value(person.isHidden), color: Value(person.color), diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 28fa63ba84..3e7fa4c2f1 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -475,6 +475,8 @@ Class | Method | HTTP request | Description - [SyncAlbumV1](doc//SyncAlbumV1.md) - [SyncAssetDeleteV1](doc//SyncAssetDeleteV1.md) - [SyncAssetExifV1](doc//SyncAssetExifV1.md) + - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) + - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) - [SyncEntityType](doc//SyncEntityType.md) - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index becafa06bf..545955a184 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -257,6 +257,8 @@ part 'model/sync_album_user_v1.dart'; part 'model/sync_album_v1.dart'; part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; +part 'model/sync_asset_face_delete_v1.dart'; +part 'model/sync_asset_face_v1.dart'; part 'model/sync_asset_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 603163f00e..55d6f4108b 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -570,6 +570,10 @@ class ApiClient { return SyncAssetDeleteV1.fromJson(value); case 'SyncAssetExifV1': return SyncAssetExifV1.fromJson(value); + case 'SyncAssetFaceDeleteV1': + return SyncAssetFaceDeleteV1.fromJson(value); + case 'SyncAssetFaceV1': + return SyncAssetFaceV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); case 'SyncEntityType': diff --git a/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart b/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart new file mode 100644 index 0000000000..0992bfdcba --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart @@ -0,0 +1,99 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetFaceDeleteV1 { + /// Returns a new [SyncAssetFaceDeleteV1] instance. + SyncAssetFaceDeleteV1({ + required this.assetFaceId, + }); + + String assetFaceId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetFaceDeleteV1 && + other.assetFaceId == assetFaceId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetFaceId.hashCode); + + @override + String toString() => 'SyncAssetFaceDeleteV1[assetFaceId=$assetFaceId]'; + + Map toJson() { + final json = {}; + json[r'assetFaceId'] = this.assetFaceId; + return json; + } + + /// Returns a new [SyncAssetFaceDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetFaceDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetFaceDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetFaceDeleteV1( + assetFaceId: mapValueOfType(json, r'assetFaceId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetFaceDeleteV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetFaceDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetFaceDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetFaceDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetFaceId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_face_v1.dart b/mobile/openapi/lib/model/sync_asset_face_v1.dart new file mode 100644 index 0000000000..853a8a1514 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_face_v1.dart @@ -0,0 +1,175 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetFaceV1 { + /// Returns a new [SyncAssetFaceV1] instance. + SyncAssetFaceV1({ + required this.assetId, + required this.boundingBoxX1, + required this.boundingBoxX2, + required this.boundingBoxY1, + required this.boundingBoxY2, + required this.id, + required this.imageHeight, + required this.imageWidth, + required this.personId, + required this.sourceType, + }); + + String assetId; + + num boundingBoxX1; + + num boundingBoxX2; + + num boundingBoxY1; + + num boundingBoxY2; + + String id; + + num imageHeight; + + num imageWidth; + + String? personId; + + String sourceType; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetFaceV1 && + other.assetId == assetId && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxY2 == boundingBoxY2 && + other.id == id && + other.imageHeight == imageHeight && + other.imageWidth == imageWidth && + other.personId == personId && + other.sourceType == sourceType; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (boundingBoxX1.hashCode) + + (boundingBoxX2.hashCode) + + (boundingBoxY1.hashCode) + + (boundingBoxY2.hashCode) + + (id.hashCode) + + (imageHeight.hashCode) + + (imageWidth.hashCode) + + (personId == null ? 0 : personId!.hashCode) + + (sourceType.hashCode); + + @override + String toString() => 'SyncAssetFaceV1[assetId=$assetId, boundingBoxX1=$boundingBoxX1, boundingBoxX2=$boundingBoxX2, boundingBoxY1=$boundingBoxY1, boundingBoxY2=$boundingBoxY2, id=$id, imageHeight=$imageHeight, imageWidth=$imageWidth, personId=$personId, sourceType=$sourceType]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'boundingBoxX1'] = this.boundingBoxX1; + json[r'boundingBoxX2'] = this.boundingBoxX2; + json[r'boundingBoxY1'] = this.boundingBoxY1; + json[r'boundingBoxY2'] = this.boundingBoxY2; + json[r'id'] = this.id; + json[r'imageHeight'] = this.imageHeight; + json[r'imageWidth'] = this.imageWidth; + if (this.personId != null) { + json[r'personId'] = this.personId; + } else { + // json[r'personId'] = null; + } + json[r'sourceType'] = this.sourceType; + return json; + } + + /// Returns a new [SyncAssetFaceV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetFaceV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetFaceV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetFaceV1( + assetId: mapValueOfType(json, r'assetId')!, + boundingBoxX1: num.parse('${json[r'boundingBoxX1']}'), + boundingBoxX2: num.parse('${json[r'boundingBoxX2']}'), + boundingBoxY1: num.parse('${json[r'boundingBoxY1']}'), + boundingBoxY2: num.parse('${json[r'boundingBoxY2']}'), + id: mapValueOfType(json, r'id')!, + imageHeight: num.parse('${json[r'imageHeight']}'), + imageWidth: num.parse('${json[r'imageWidth']}'), + personId: mapValueOfType(json, r'personId'), + sourceType: mapValueOfType(json, r'sourceType')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetFaceV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetFaceV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetFaceV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetFaceV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'boundingBoxX1', + 'boundingBoxX2', + 'boundingBoxY1', + 'boundingBoxY2', + 'id', + 'imageHeight', + 'imageWidth', + 'personId', + 'sourceType', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 61f94401c7..65ed78105c 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -58,6 +58,8 @@ class SyncEntityType { static const stackDeleteV1 = SyncEntityType._(r'StackDeleteV1'); static const personV1 = SyncEntityType._(r'PersonV1'); static const personDeleteV1 = SyncEntityType._(r'PersonDeleteV1'); + static const assetFaceV1 = SyncEntityType._(r'AssetFaceV1'); + static const assetFaceDeleteV1 = SyncEntityType._(r'AssetFaceDeleteV1'); static const userMetadataV1 = SyncEntityType._(r'UserMetadataV1'); static const userMetadataDeleteV1 = SyncEntityType._(r'UserMetadataDeleteV1'); static const syncAckV1 = SyncEntityType._(r'SyncAckV1'); @@ -100,6 +102,8 @@ class SyncEntityType { stackDeleteV1, personV1, personDeleteV1, + assetFaceV1, + assetFaceDeleteV1, userMetadataV1, userMetadataDeleteV1, syncAckV1, @@ -177,6 +181,8 @@ class SyncEntityTypeTypeTransformer { case r'StackDeleteV1': return SyncEntityType.stackDeleteV1; case r'PersonV1': return SyncEntityType.personV1; case r'PersonDeleteV1': return SyncEntityType.personDeleteV1; + case r'AssetFaceV1': return SyncEntityType.assetFaceV1; + case r'AssetFaceDeleteV1': return SyncEntityType.assetFaceDeleteV1; case r'UserMetadataV1': return SyncEntityType.userMetadataV1; case r'UserMetadataDeleteV1': return SyncEntityType.userMetadataDeleteV1; case r'SyncAckV1': return SyncEntityType.syncAckV1; diff --git a/mobile/openapi/lib/model/sync_person_v1.dart b/mobile/openapi/lib/model/sync_person_v1.dart index e86c22f64b..6749beb3e1 100644 --- a/mobile/openapi/lib/model/sync_person_v1.dart +++ b/mobile/openapi/lib/model/sync_person_v1.dart @@ -22,7 +22,6 @@ class SyncPersonV1 { required this.isHidden, required this.name, required this.ownerId, - required this.thumbnailPath, required this.updatedAt, }); @@ -44,8 +43,6 @@ class SyncPersonV1 { String ownerId; - String thumbnailPath; - DateTime updatedAt; @override @@ -59,7 +56,6 @@ class SyncPersonV1 { other.isHidden == isHidden && other.name == name && other.ownerId == ownerId && - other.thumbnailPath == thumbnailPath && other.updatedAt == updatedAt; @override @@ -74,11 +70,10 @@ class SyncPersonV1 { (isHidden.hashCode) + (name.hashCode) + (ownerId.hashCode) + - (thumbnailPath.hashCode) + (updatedAt.hashCode); @override - String toString() => 'SyncPersonV1[birthDate=$birthDate, color=$color, createdAt=$createdAt, faceAssetId=$faceAssetId, id=$id, isFavorite=$isFavorite, isHidden=$isHidden, name=$name, ownerId=$ownerId, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt]'; + String toString() => 'SyncPersonV1[birthDate=$birthDate, color=$color, createdAt=$createdAt, faceAssetId=$faceAssetId, id=$id, isFavorite=$isFavorite, isHidden=$isHidden, name=$name, ownerId=$ownerId, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -103,7 +98,6 @@ class SyncPersonV1 { json[r'isHidden'] = this.isHidden; json[r'name'] = this.name; json[r'ownerId'] = this.ownerId; - json[r'thumbnailPath'] = this.thumbnailPath; json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); return json; } @@ -126,7 +120,6 @@ class SyncPersonV1 { isHidden: mapValueOfType(json, r'isHidden')!, name: mapValueOfType(json, r'name')!, ownerId: mapValueOfType(json, r'ownerId')!, - thumbnailPath: mapValueOfType(json, r'thumbnailPath')!, updatedAt: mapDateTime(json, r'updatedAt', r'')!, ); } @@ -184,7 +177,6 @@ class SyncPersonV1 { 'isHidden', 'name', 'ownerId', - 'thumbnailPath', 'updatedAt', }; } diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 75ce852f9f..800b3f4485 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -39,6 +39,7 @@ class SyncRequestType { static const stacksV1 = SyncRequestType._(r'StacksV1'); static const usersV1 = SyncRequestType._(r'UsersV1'); static const peopleV1 = SyncRequestType._(r'PeopleV1'); + static const assetFacesV1 = SyncRequestType._(r'AssetFacesV1'); static const userMetadataV1 = SyncRequestType._(r'UserMetadataV1'); /// List of all possible values in this [enum][SyncRequestType]. @@ -59,6 +60,7 @@ class SyncRequestType { stacksV1, usersV1, peopleV1, + assetFacesV1, userMetadataV1, ]; @@ -114,6 +116,7 @@ class SyncRequestTypeTypeTransformer { case r'StacksV1': return SyncRequestType.stacksV1; case r'UsersV1': return SyncRequestType.usersV1; case r'PeopleV1': return SyncRequestType.peopleV1; + case r'AssetFacesV1': return SyncRequestType.assetFacesV1; case r'UserMetadataV1': return SyncRequestType.userMetadataV1; default: if (!allowNull) { diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 71329c3f75..2f41318d6d 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13788,6 +13788,65 @@ ], "type": "object" }, + "SyncAssetFaceDeleteV1": { + "properties": { + "assetFaceId": { + "type": "string" + } + }, + "required": [ + "assetFaceId" + ], + "type": "object" + }, + "SyncAssetFaceV1": { + "properties": { + "assetId": { + "type": "string" + }, + "boundingBoxX1": { + "type": "number" + }, + "boundingBoxX2": { + "type": "number" + }, + "boundingBoxY1": { + "type": "number" + }, + "boundingBoxY2": { + "type": "number" + }, + "id": { + "type": "string" + }, + "imageHeight": { + "type": "number" + }, + "imageWidth": { + "type": "number" + }, + "personId": { + "nullable": true, + "type": "string" + }, + "sourceType": { + "type": "string" + } + }, + "required": [ + "assetId", + "boundingBoxX1", + "boundingBoxX2", + "boundingBoxY1", + "boundingBoxY2", + "id", + "imageHeight", + "imageWidth", + "personId", + "sourceType" + ], + "type": "object" + }, "SyncAssetV1": { "properties": { "checksum": { @@ -13912,6 +13971,8 @@ "StackDeleteV1", "PersonV1", "PersonDeleteV1", + "AssetFaceV1", + "AssetFaceDeleteV1", "UserMetadataV1", "UserMetadataDeleteV1", "SyncAckV1", @@ -14109,9 +14170,6 @@ "ownerId": { "type": "string" }, - "thumbnailPath": { - "type": "string" - }, "updatedAt": { "format": "date-time", "type": "string" @@ -14127,7 +14185,6 @@ "isHidden", "name", "ownerId", - "thumbnailPath", "updatedAt" ], "type": "object" @@ -14150,6 +14207,7 @@ "StacksV1", "UsersV1", "PeopleV1", + "AssetFacesV1", "UserMetadataV1" ], "type": "string" diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index f60fa6dfe8..d5f7fde52a 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -4125,6 +4125,8 @@ export enum SyncEntityType { StackDeleteV1 = "StackDeleteV1", PersonV1 = "PersonV1", PersonDeleteV1 = "PersonDeleteV1", + AssetFaceV1 = "AssetFaceV1", + AssetFaceDeleteV1 = "AssetFaceDeleteV1", UserMetadataV1 = "UserMetadataV1", UserMetadataDeleteV1 = "UserMetadataDeleteV1", SyncAckV1 = "SyncAckV1", @@ -4147,6 +4149,7 @@ export enum SyncRequestType { StacksV1 = "StacksV1", UsersV1 = "UsersV1", PeopleV1 = "PeopleV1", + AssetFacesV1 = "AssetFacesV1", UserMetadataV1 = "UserMetadataV1" } export enum TranscodeHWAccel { diff --git a/server/src/database.ts b/server/src/database.ts index d42b2618a4..dc99fc5b31 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -272,6 +272,8 @@ export type AssetFace = { personId: string | null; sourceType: SourceType; person?: Person | null; + updatedAt: Date; + updateId: string; }; const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const; diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 9725539e3d..e0c9c059c4 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -245,7 +245,6 @@ export class SyncPersonV1 { ownerId!: string; name!: string; birthDate!: Date | null; - thumbnailPath!: string; isHidden!: boolean; isFavorite!: boolean; color!: string | null; @@ -257,6 +256,25 @@ export class SyncPersonDeleteV1 { personId!: string; } +@ExtraModel() +export class SyncAssetFaceV1 { + id!: string; + assetId!: string; + personId!: string | null; + imageWidth!: number; + imageHeight!: number; + boundingBoxX1!: number; + boundingBoxY1!: number; + boundingBoxX2!: number; + boundingBoxY2!: number; + sourceType!: string; +} + +@ExtraModel() +export class SyncAssetFaceDeleteV1 { + assetFaceId!: string; +} + @ExtraModel() export class SyncUserMetadataV1 { userId!: string; @@ -312,6 +330,8 @@ export type SyncItem = { [SyncEntityType.PartnerStackV1]: SyncStackV1; [SyncEntityType.PersonV1]: SyncPersonV1; [SyncEntityType.PersonDeleteV1]: SyncPersonDeleteV1; + [SyncEntityType.AssetFaceV1]: SyncAssetFaceV1; + [SyncEntityType.AssetFaceDeleteV1]: SyncAssetFaceDeleteV1; [SyncEntityType.UserMetadataV1]: SyncUserMetadataV1; [SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1; [SyncEntityType.SyncAckV1]: SyncAckV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index e41a790999..f2eae615ab 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -568,6 +568,7 @@ export enum SyncRequestType { StacksV1 = 'StacksV1', UsersV1 = 'UsersV1', PeopleV1 = 'PeopleV1', + AssetFacesV1 = 'AssetFacesV1', UserMetadataV1 = 'UserMetadataV1', } @@ -619,6 +620,9 @@ export enum SyncEntityType { PersonV1 = 'PersonV1', PersonDeleteV1 = 'PersonDeleteV1', + AssetFaceV1 = 'AssetFaceV1', + AssetFaceDeleteV1 = 'AssetFaceDeleteV1', + UserMetadataV1 = 'UserMetadataV1', UserMetadataDeleteV1 = 'UserMetadataDeleteV1', diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 4782eedf1d..7502b79f57 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -409,6 +409,41 @@ where order by "updateId" asc +-- SyncRepository.assetFace.getDeletes +select + "asset_face_audit"."id", + "assetFaceId" +from + "asset_face_audit" + left join "asset" on "asset"."id" = "asset_face_audit"."assetId" +where + "asset"."ownerId" = $1 + and "asset_face_audit"."deletedAt" < now() - interval '1 millisecond' +order by + "asset_face_audit"."id" asc + +-- SyncRepository.assetFace.getUpserts +select + "asset_face"."id", + "assetId", + "personId", + "imageWidth", + "imageHeight", + "boundingBoxX1", + "boundingBoxY1", + "boundingBoxX2", + "boundingBoxY2", + "sourceType", + "asset_face"."updateId" +from + "asset_face" + left join "asset" on "asset"."id" = "asset_face"."assetId" +where + "asset_face"."updatedAt" < now() - interval '1 millisecond' + and "asset"."ownerId" = $1 +order by + "asset_face"."updateId" asc + -- SyncRepository.memory.getDeletes select "id", @@ -779,7 +814,6 @@ select "ownerId", "name", "birthDate", - "thumbnailPath", "isHidden", "isFavorite", "color", diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 34c450d52d..dba52d25a0 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -17,7 +17,8 @@ type AuditTables = | 'memory_asset_audit' | 'stack_audit' | 'person_audit' - | 'user_metadata_audit'; + | 'user_metadata_audit' + | 'asset_face_audit'; type UpsertTables = | 'user' | 'partner' @@ -29,7 +30,8 @@ type UpsertTables = | 'memory_asset' | 'stack' | 'person' - | 'user_metadata'; + | 'user_metadata' + | 'asset_face'; @Injectable() export class SyncRepository { @@ -40,6 +42,7 @@ export class SyncRepository { albumUser: AlbumUserSync; asset: AssetSync; assetExif: AssetExifSync; + assetFace: AssetFaceSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; @@ -59,6 +62,7 @@ export class SyncRepository { this.albumUser = new AlbumUserSync(this.db); this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); + this.assetFace = new AssetFaceSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); @@ -385,7 +389,6 @@ class PersonSync extends BaseSync { 'ownerId', 'name', 'birthDate', - 'thumbnailPath', 'isHidden', 'isFavorite', 'color', @@ -398,6 +401,46 @@ class PersonSync extends BaseSync { } } +class AssetFaceSync extends BaseSync { + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getDeletes(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('asset_face_audit') + .select(['asset_face_audit.id', 'assetFaceId']) + .orderBy('asset_face_audit.id', 'asc') + .leftJoin('asset', 'asset.id', 'asset_face_audit.assetId') + .where('asset.ownerId', '=', userId) + .where('asset_face_audit.deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('asset_face_audit.id', '>', ack!.updateId)) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getUpserts(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('asset_face') + .select([ + 'asset_face.id', + 'assetId', + 'personId', + 'imageWidth', + 'imageHeight', + 'boundingBoxX1', + 'boundingBoxY1', + 'boundingBoxX2', + 'boundingBoxY2', + 'sourceType', + 'asset_face.updateId', + ]) + .where('asset_face.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('asset_face.updateId', '>', ack!.updateId)) + .orderBy('asset_face.updateId', 'asc') + .leftJoin('asset', 'asset.id', 'asset_face.assetId') + .where('asset.ownerId', '=', userId) + .stream(); + } +} + class AssetExifSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getUpserts(userId: string, ack?: SyncAck) { diff --git a/server/src/schema/functions.ts b/server/src/schema/functions.ts index 5577169227..786e7a1ffa 100644 --- a/server/src/schema/functions.ts +++ b/server/src/schema/functions.ts @@ -229,3 +229,16 @@ export const user_metadata_audit = registerFunction({ RETURN NULL; END`, }); + +export const asset_face_audit = registerFunction({ + name: 'asset_face_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO asset_face_audit ("assetFaceId", "assetId") + SELECT "id", "assetId" + FROM OLD; + RETURN NULL; + END`, +}); diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index ba25a65d4d..8982437b34 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -4,6 +4,7 @@ import { album_user_after_insert, album_user_delete_audit, asset_delete_audit, + asset_face_audit, f_concat_ws, f_unaccent, immich_uuid_v7, @@ -27,6 +28,7 @@ import { AlbumTable } from 'src/schema/tables/album.table'; import { ApiKeyTable } from 'src/schema/tables/api-key.table'; import { AssetAuditTable } from 'src/schema/tables/asset-audit.table'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; +import { AssetFaceAuditTable } from 'src/schema/tables/asset-face-audit.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; @@ -78,6 +80,7 @@ export class ImmichDatabase { ApiKeyTable, AssetAuditTable, AssetFaceTable, + AssetFaceAuditTable, AssetJobStatusTable, AssetTable, AssetFileTable, @@ -132,6 +135,7 @@ export class ImmichDatabase { stack_delete_audit, person_delete_audit, user_metadata_audit, + asset_face_audit, ]; enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum]; @@ -158,6 +162,7 @@ export interface DB { asset: AssetTable; asset_exif: AssetExifTable; asset_face: AssetFaceTable; + asset_face_audit: AssetFaceAuditTable; asset_file: AssetFileTable; asset_job_status: AssetJobStatusTable; asset_audit: AssetAuditTable; diff --git a/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts b/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts new file mode 100644 index 0000000000..1f4072e34e --- /dev/null +++ b/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts @@ -0,0 +1,52 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE OR REPLACE FUNCTION asset_face_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO asset_face_audit ("assetFaceId", "assetId") + SELECT "id", "assetId" + FROM OLD; + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE TABLE "asset_face_audit" ( + "id" uuid NOT NULL DEFAULT immich_uuid_v7(), + "assetFaceId" uuid NOT NULL, + "assetId" uuid NOT NULL, + "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), + CONSTRAINT "asset_face_audit_pkey" PRIMARY KEY ("id") +);`.execute(db); + await sql`CREATE INDEX "asset_face_audit_assetFaceId_idx" ON "asset_face_audit" ("assetFaceId");`.execute(db); + await sql`CREATE INDEX "asset_face_audit_assetId_idx" ON "asset_face_audit" ("assetId");`.execute(db); + await sql`CREATE INDEX "asset_face_audit_deletedAt_idx" ON "asset_face_audit" ("deletedAt");`.execute(db); + await sql`ALTER TABLE "asset_face" ADD "updatedAt" timestamp with time zone NOT NULL DEFAULT now();`.execute(db); + await sql`ALTER TABLE "asset_face" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_face_audit" + AFTER DELETE ON "asset_face" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION asset_face_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_face_updatedAt" + BEFORE UPDATE ON "asset_face" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('function_asset_face_audit', '{"type":"function","name":"asset_face_audit","sql":"CREATE OR REPLACE FUNCTION asset_face_audit()\\n RETURNS TRIGGER\\n LANGUAGE PLPGSQL\\n AS $$\\n BEGIN\\n INSERT INTO asset_face_audit (\\"assetFaceId\\", \\"assetId\\")\\n SELECT \\"id\\", \\"assetId\\"\\n FROM OLD;\\n RETURN NULL;\\n END\\n $$;"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_face_audit', '{"type":"trigger","name":"asset_face_audit","sql":"CREATE OR REPLACE TRIGGER \\"asset_face_audit\\"\\n AFTER DELETE ON \\"asset_face\\"\\n REFERENCING OLD TABLE AS \\"old\\"\\n FOR EACH STATEMENT\\n WHEN (pg_trigger_depth() = 0)\\n EXECUTE FUNCTION asset_face_audit();"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_face_updatedAt', '{"type":"trigger","name":"asset_face_updatedAt","sql":"CREATE OR REPLACE TRIGGER \\"asset_face_updatedAt\\"\\n BEFORE UPDATE ON \\"asset_face\\"\\n FOR EACH ROW\\n EXECUTE FUNCTION updated_at();"}'::jsonb);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TRIGGER "asset_face_audit" ON "asset_face";`.execute(db); + await sql`DROP TRIGGER "asset_face_updatedAt" ON "asset_face";`.execute(db); + await sql`ALTER TABLE "asset_face" DROP COLUMN "updatedAt";`.execute(db); + await sql`ALTER TABLE "asset_face" DROP COLUMN "updateId";`.execute(db); + await sql`DROP TABLE "asset_face_audit";`.execute(db); + await sql`DROP FUNCTION asset_face_audit;`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'function_asset_face_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_face_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_face_updatedAt';`.execute(db); +} diff --git a/server/src/schema/tables/asset-face-audit.table.ts b/server/src/schema/tables/asset-face-audit.table.ts new file mode 100644 index 0000000000..4f03c22aa0 --- /dev/null +++ b/server/src/schema/tables/asset-face-audit.table.ts @@ -0,0 +1,17 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; + +@Table('asset_face_audit') +export class AssetFaceAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: Generated; + + @Column({ type: 'uuid', index: true }) + assetFaceId!: string; + + @Column({ type: 'uuid', index: true }) + assetId!: string; + + @CreateDateColumn({ default: () => 'clock_timestamp()', index: true }) + deletedAt!: Generated; +} diff --git a/server/src/schema/tables/asset-face.table.ts b/server/src/schema/tables/asset-face.table.ts index 6e45a3a64d..5041d945e2 100644 --- a/server/src/schema/tables/asset-face.table.ts +++ b/server/src/schema/tables/asset-face.table.ts @@ -1,8 +1,11 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { SourceType } from 'src/enum'; import { asset_face_source_type } from 'src/schema/enums'; +import { asset_face_audit } from 'src/schema/functions'; import { AssetTable } from 'src/schema/tables/asset.table'; import { PersonTable } from 'src/schema/tables/person.table'; import { + AfterDeleteTrigger, Column, DeleteDateColumn, ForeignKeyColumn, @@ -11,9 +14,17 @@ import { PrimaryGeneratedColumn, Table, Timestamp, + UpdateDateColumn, } from 'src/sql-tools'; @Table({ name: 'asset_face' }) +@UpdatedAtTrigger('asset_face_updatedAt') +@AfterDeleteTrigger({ + scope: 'statement', + function: asset_face_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() = 0', +}) // schemaFromDatabase does not preserve column order @Index({ name: 'asset_face_assetId_personId_idx', columns: ['assetId', 'personId'] }) @Index({ columns: ['personId', 'assetId'] }) @@ -61,4 +72,10 @@ export class AssetFaceTable { @DeleteDateColumn() deletedAt!: Timestamp | null; + + @UpdateDateColumn() + updatedAt!: Generated; + + @UpdateIdColumn() + updateId!: Generated; } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 4463ab0d76..fb582ab038 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -70,6 +70,7 @@ export const SYNC_TYPES_ORDER = [ SyncRequestType.MemoriesV1, SyncRequestType.MemoryToAssetsV1, SyncRequestType.PeopleV1, + SyncRequestType.AssetFacesV1, SyncRequestType.UserMetadataV1, ]; @@ -156,6 +157,7 @@ export class SyncService extends BaseService { [SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth), [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, session.id), [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(response, checkpointMap, auth), + [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(response, checkpointMap, auth), [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(response, checkpointMap, auth), }; @@ -606,6 +608,20 @@ export class SyncService extends BaseService { } } + private async syncAssetFacesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + const deleteType = SyncEntityType.AssetFaceDeleteV1; + const deletes = this.syncRepository.assetFace.getDeletes(auth.user.id, checkpointMap[deleteType]); + for await (const { id, ...data } of deletes) { + send(response, { type: deleteType, ids: [id], data }); + } + + const upsertType = SyncEntityType.AssetFaceV1; + const upserts = this.syncRepository.assetFace.getUpserts(auth.user.id, checkpointMap[upsertType]); + for await (const { updateId, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data }); + } + } + private async syncUserMetadataV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { const deleteType = SyncEntityType.UserMetadataDeleteV1; const deletes = this.syncRepository.userMetadata.getDeletes(auth.user.id, checkpointMap[deleteType]); diff --git a/server/test/fixtures/face.stub.ts b/server/test/fixtures/face.stub.ts index beecf7c69e..f655a3944e 100644 --- a/server/test/fixtures/face.stub.ts +++ b/server/test/fixtures/face.stub.ts @@ -23,6 +23,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId1', embedding: '[1, 2, 3, 4]' }, deletedAt: new Date(), + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), primaryFace1: Object.freeze({ id: 'assetFaceId2', @@ -39,6 +41,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId2', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), mergeFace1: Object.freeze({ id: 'assetFaceId3', @@ -55,6 +59,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId3', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), noPerson1: Object.freeze({ id: 'assetFaceId8', @@ -71,6 +77,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId8', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), noPerson2: Object.freeze({ id: 'assetFaceId9', @@ -87,6 +95,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId9', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), fromExif1: Object.freeze({ id: 'assetFaceId9', @@ -102,6 +112,8 @@ export const faceStub = { imageWidth: 400, sourceType: SourceType.Exif, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), fromExif2: Object.freeze({ id: 'assetFaceId9', @@ -117,6 +129,8 @@ export const faceStub = { imageWidth: 1024, sourceType: SourceType.Exif, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), withBirthDate: Object.freeze({ id: 'assetFaceId10', @@ -132,5 +146,7 @@ export const faceStub = { imageWidth: 1024, sourceType: SourceType.MachineLearning, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), }; diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index 4d13264fa2..d6038b6b84 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -154,6 +154,12 @@ export class MediumTestContext { return { asset, result }; } + async newAssetFace(dto: Partial> & { assetId: string }) { + const assetFace = mediumFactory.assetFaceInsert(dto); + const result = await this.get(PersonRepository).createAssetFace(assetFace); + return { assetFace, result }; + } + async newMemory(dto: Partial> = {}) { const memory = mediumFactory.memoryInsert(dto); const result = await this.get(MemoryRepository).create(memory, new Set()); diff --git a/server/test/medium/specs/sync/sync-asset-face.spec.ts b/server/test/medium/specs/sync/sync-asset-face.spec.ts new file mode 100644 index 0000000000..68d3007c52 --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-face.spec.ts @@ -0,0 +1,92 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AssetFaceV1, () => { + it('should detect and sync the first asset face', async () => { + const { auth, ctx } = await setup(); + const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); + const { person } = await ctx.newPerson({ ownerId: auth.user.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id, personId: person.id }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: assetFace.id, + assetId: asset.id, + personId: person.id, + imageWidth: assetFace.imageWidth, + imageHeight: assetFace.imageHeight, + boundingBoxX1: assetFace.boundingBoxX1, + boundingBoxY1: assetFace.boundingBoxY1, + boundingBoxX2: assetFace.boundingBoxX2, + boundingBoxY2: assetFace.boundingBoxY2, + sourceType: assetFace.sourceType, + }), + type: 'AssetFaceV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + }); + + it('should detect and sync a deleted asset face', async () => { + const { auth, ctx } = await setup(); + const personRepo = ctx.get(PersonRepository); + const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id }); + await personRepo.deleteAssetFace(assetFace.id); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + assetFaceId: assetFace.id, + }, + type: 'AssetFaceDeleteV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + }); + + it('should not sync an asset face or asset face delete for an unrelated user', async () => { + const { auth, ctx } = await setup(); + const personRepo = ctx.get(PersonRepository); + const { user: user2 } = await ctx.newUser(); + const { session } = await ctx.newSession({ userId: user2.id }); + const { asset } = await ctx.newAsset({ ownerId: user2.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id }); + const auth2 = factory.auth({ session, user: user2 }); + + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); + expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + + await personRepo.deleteAssetFace(assetFace.id); + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); + expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-person.spec.ts b/server/test/medium/specs/sync/sync-person.spec.ts index 807e41894c..fbf401e377 100644 --- a/server/test/medium/specs/sync/sync-person.spec.ts +++ b/server/test/medium/specs/sync/sync-person.spec.ts @@ -31,7 +31,6 @@ describe(SyncEntityType.PersonV1, () => { data: expect.objectContaining({ id: person.id, name: person.name, - thumbnailPath: person.thumbnailPath, isHidden: person.isHidden, birthDate: person.birthDate, faceAssetId: person.faceAssetId, From 02c423b32672775c3786a69f46cb0b03bcccfd28 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Jul 2025 22:44:39 -0500 Subject: [PATCH 025/169] chore: graceful(not) disposal(be gong) Isar (#20062) --- mobile/lib/utils/isolate.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 2263bf3d76..3c2aeed756 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -63,7 +63,17 @@ Cancelable runInIsolateGentle({ try { await LogService.I.flushBuffer(); await ref.read(driftProvider).close(); - await ref.read(isarProvider).close(); + + // Close Isar safely + try { + final isar = ref.read(isarProvider); + if (isar.isOpen) { + await isar.close(); + } + } catch (e) { + debugPrint("Error closing Isar: $e"); + } + ref.dispose(); } catch (error) { debugPrint("Error closing resources in isolate: $error"); From 30f6dc3a6e8b794d17678491bda904c858522f59 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Tue, 22 Jul 2025 11:27:32 +0200 Subject: [PATCH 026/169] chore(web): update translations (#19228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 1024mb <1024mb@users.noreply.hosted.weblate.org> Co-authored-by: Abel Márquez Mora Co-authored-by: Adupa Vasista Co-authored-by: AgentTricky Co-authored-by: Ahmed Khaleel Shihab Co-authored-by: Ahmed Shihab Co-authored-by: Alberto Serluca Co-authored-by: Alex Co-authored-by: Andreas Johansen Co-authored-by: Antonio Vazquez Co-authored-by: Apurbo Islam Co-authored-by: Bahadır Hamza Öztürk Co-authored-by: Barend van der Walt Co-authored-by: Bernardo Co-authored-by: Bezruchenko Simon Co-authored-by: Bora Atıcı Co-authored-by: Carina Chenot Co-authored-by: Christian Glockner Co-authored-by: Claudiu Hanza Co-authored-by: Cristality_ Co-authored-by: DarkWolf DarkINFINITE Co-authored-by: Davide Ciaccia Co-authored-by: Denis Pacquier Co-authored-by: DevServs Co-authored-by: Etienne de Villiers Co-authored-by: Filip Polak Co-authored-by: Fjuro Co-authored-by: Fjuro Co-authored-by: Florian Ostertag Co-authored-by: G M Co-authored-by: Gerry Co-authored-by: Girom Kenji Respicio Pacho Co-authored-by: Gyubin Lee Co-authored-by: Hazret Co-authored-by: Hoi Co-authored-by: Hurricane-32 Co-authored-by: Indrek Haav Co-authored-by: Ivan Dimitrov Co-authored-by: JPar99 Co-authored-by: Janat Taerakul Co-authored-by: Janat Taerakul Co-authored-by: Jozef Gaal Co-authored-by: JustRensio Co-authored-by: Lenny Angst Co-authored-by: Leo Bottaro Co-authored-by: Lucas Correia Co-authored-by: Luis Peregrina Co-authored-by: MSDNicrosoft Co-authored-by: MaBeniu Co-authored-by: Marc Portabella Navarro Co-authored-by: Martin Co-authored-by: Mateo Varela Co-authored-by: Matjaž T Co-authored-by: Matteo Marchi Co-authored-by: Mehedi Hasan Co-authored-by: Mohammed Al Otaibi Co-authored-by: Molnar Eduard Co-authored-by: Máté Molnár Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Niko Savola Co-authored-by: OffsetMonkey538 Co-authored-by: P Co-authored-by: Pavel Miniutka Co-authored-by: Petri Hämäläinen Co-authored-by: Revc Nix Co-authored-by: Robert Co-authored-by: Runskrift Co-authored-by: SGT Co-authored-by: Sachin Kekarjawalekar Co-authored-by: Sai varun Co-authored-by: Santiago Co-authored-by: Sergey Katsubo Co-authored-by: Sergi Font Co-authored-by: Shawn Co-authored-by: Sylvain Pichon Co-authored-by: Taiki M Co-authored-by: Theodor Onarheim Co-authored-by: Tijs-B Co-authored-by: Tomas Veselis Co-authored-by: Tony Ronaldo Matute Co-authored-by: Topaz Barziv Co-authored-by: User 123456789 Co-authored-by: Valter Vicente Co-authored-by: Vegard Fladby Co-authored-by: Xo Co-authored-by: Yago Raña Gayoso Co-authored-by: adri1m64 Co-authored-by: adriadam10 Co-authored-by: amirulashraf3861 Co-authored-by: anton garcias Co-authored-by: ardtas Co-authored-by: bacardicoke Co-authored-by: catelixor Co-authored-by: dark&white Co-authored-by: eSascha Co-authored-by: eav5jhl0 Co-authored-by: evensure Co-authored-by: jicetus Co-authored-by: juanCoder64 Co-authored-by: lumppu Co-authored-by: mastoduy Co-authored-by: miiyuh Co-authored-by: no Co-authored-by: sarga Co-authored-by: slick-daddy Co-authored-by: st7105 Co-authored-by: waclaw66 Co-authored-by: wickdj Co-authored-by: Вячеслав Лукьяненко Co-authored-by: Оргил Пүрэвдорж --- i18n/af.json | 118 +++++++++- i18n/ar.json | 470 ++++++++++++++++++++++++++++++------- i18n/be.json | 276 +++++++++++++++++++++- i18n/bg.json | 42 +++- i18n/bn.json | 82 ++++++- i18n/ca.json | 24 +- i18n/cs.json | 69 +++++- i18n/da.json | 4 +- i18n/de.json | 54 ++++- i18n/el.json | 2 - i18n/es.json | 90 ++++--- i18n/et.json | 75 ++++-- i18n/fi.json | 73 +++++- i18n/fil.json | 3 + i18n/fr.json | 59 ++++- i18n/gl.json | 1 - i18n/he.json | 37 ++- i18n/hi.json | 2 + i18n/hr.json | 1 - i18n/hu.json | 163 ++++++++++--- i18n/id.json | 23 +- i18n/it.json | 115 +++++---- i18n/ja.json | 12 +- i18n/ko.json | 39 ++- i18n/lt.json | 99 ++++++-- i18n/lv.json | 84 ++++++- i18n/mn.json | 8 + i18n/mr.json | 19 +- i18n/ms.json | 117 +++++++-- i18n/nb_NO.json | 127 ++++++---- i18n/nl.json | 55 ++++- i18n/nn.json | 59 ++++- i18n/pl.json | 42 +++- i18n/pt.json | 43 +++- i18n/pt_BR.json | 136 ++++++----- i18n/ro.json | 134 +++++++++-- i18n/ru.json | 77 ++++-- i18n/sk.json | 508 +++++++++++++++++++++++++++------------- i18n/sl.json | 49 +++- i18n/sr_Cyrl.json | 1 - i18n/sr_Latn.json | 1 - i18n/sv.json | 24 +- i18n/ta.json | 1 - i18n/te.json | 15 +- i18n/th.json | 219 ++++++++++++++--- i18n/tr.json | 202 +++++++++++++--- i18n/uk.json | 3 +- i18n/vi.json | 20 +- i18n/zh_Hant.json | 2 - i18n/zh_SIMPLIFIED.json | 49 +++- 50 files changed, 3168 insertions(+), 760 deletions(-) diff --git a/i18n/af.json b/i18n/af.json index 55555c8398..b3729903ec 100644 --- a/i18n/af.json +++ b/i18n/af.json @@ -4,6 +4,7 @@ "account_settings": "Rekeninginstellings", "acknowledge": "Erken", "action": "Aksie", + "action_common_update": "Opdateur", "actions": "Aksies", "active": "Aktief", "activity": "Aktiwiteite", @@ -13,6 +14,7 @@ "add_a_location": "Voeg 'n ligging by", "add_a_name": "Voeg 'n naam by", "add_a_title": "Voeg 'n titel by", + "add_endpoint": "Voeg Koppelvlakpunt by", "add_exclusion_pattern": "Voeg uitsgluitingspatrone by", "add_import_path": "Voeg invoerpad by", "add_location": "Voeg ligging by", @@ -20,26 +22,30 @@ "add_partner": "Voeg vennoot by", "add_path": "Voeg pad by", "add_photos": "Voeg foto's by", + "add_tag": "Voeg tag by", "add_to": "Voeg by…", "add_to_album": "Voeg na album", - "add_to_shared_album": "Voeg na gedeelde album", + "add_to_album_bottom_sheet_added": "By {album} bygevoeg", + "add_to_album_bottom_sheet_already_exists": "Reeds in {album}", + "add_to_shared_album": "Voeg toe aan gedeelde album", "add_url": "Voeg URL by", - "added_to_archive": "By argief gevoeg", - "added_to_favorites": "By gunstelinge gevoeg", - "added_to_favorites_count": "Het {count, number} by gunstelinge gevoeg", + "added_to_archive": "By argief toegevoegd", + "added_to_favorites": "By gunstelinge toegevoegd", + "added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd", "admin": { "add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".", + "admin_user": "Admin gebruiker", "asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.", "authentication_settings": "Verifikasie instellings", "authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings", "authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.", "authentication_settings_reenable": "Om te heraktiveer, gebruik 'n Server Command.", "background_task_job": "Agtergrondtake", - "backup_database": "Rugsteun databasis", + "backup_database": "Skep Datastortlêer", "backup_database_enable_description": "Aktiveer databasisrugsteun", "backup_keep_last_amount": "Aantal vorige rugsteune om te hou", "backup_settings": "Rugsteun instellings", - "backup_settings_description": "Bestuur databasis rugsteun instellings", + "backup_settings_description": "Bestuur databasis rugsteun instellings.", "cleared_jobs": "Poste gevee vir: {job}", "config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel", "confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?", @@ -47,6 +53,7 @@ "confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder", "confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.", "confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?", + "confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?", "create_job": "Skep werk", "cron_expression": "Cron uitdrukking", "cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. Crontab Guru", @@ -56,10 +63,14 @@ "exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.", "external_library_management": "Eksterne Biblioteekbestuur", "face_detection": "Gesig deteksie", + "face_detection_description": "Detecteer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder boonop alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Gedekte gesigte sal ná voltooiing van Gesigdetectie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.", + "facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.", "failed_job_command": "Opdrag {command} het misluk vir werk: {job}", "force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.", "image_format": "Formaat", "image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.", + "image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem", + "image_fullsize_enabled": "Skakel aan vol grote prent generasie", "image_prefer_embedded_preview": "Verkies ingebedde voorskou", "image_prefer_wide_gamut": "Verkies wide gamut", "image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.", @@ -77,8 +88,99 @@ "job_concurrency": "{job} gelyktydigheid", "job_created": "Taak gemaak", "job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.", - "job_settings": "Agtergrondtaakinstellings" + "job_settings": "Agtergrondtaakinstellings", + "job_settings_description": "Bestuur werkgelyktydigheid", + "job_status": "Werkstatus", + "library_created": "Biblioteek geskep: {library}", + "library_deleted": "Biblioteek verwyder", + "library_import_path_description": "Spesifiseer 'n leer om in te neem. Hierdie leer, en al die sub leers, gaan geskandeer for vir prente en videos.", + "library_scanning": "Periodieke Skandering", + "library_scanning_description": "Stel periodieke skandering van biblioteek in", + "library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering", + "library_settings": "Eksterne Biblioteek", + "map_settings": "Kaart", + "migration_job": "Migrasie", + "oauth_settings": "OAuth", + "transcoding_acceleration_vaapi": "VAAPI" }, + "administration": "Administrasie", + "advanced": "Gevorderde", + "albums": "Albums", + "all": "Alle", + "anti_clockwise": "Anti-kloksgewys", + "archive": "Argief", + "asset_skipped": "Oorgeslaan", + "asset_uploaded": "Opgelaai", + "asset_uploading": "Oplaai…", + "assets": "Bates", + "back": "Terug", + "backward": "Agteruit", + "build": "Bou", + "camera": "Kamera", + "cancel": "Kanselleer", + "city": "Stad", + "clockwise": "Kloksgewys", + "close": "Maak toe", + "color": "Kleur", + "confirm": "Bevestig", + "contain": "Bevat", + "context": "Konteks", + "continue": "Gaan voort", + "country": "Land", + "cover": "Bedek", + "create": "Skep", + "created": "Geskep", + "dark": "Donker", + "day": "Dag", + "delete": "Verwyder", + "description": "Beskrywing", + "details": "Besonderhede", + "direction": "Rigting", + "discover": "Ontdek", + "documentation": "Dokumentasie", + "done": "Klaar", + "download": "Aflaai", + "download_settings": "Aflaai", + "duplicates": "Duplikate", + "duration": "Duur", + "edit": "Wysig", + "edited": "Gewysigd", "search_by_description": "Soek by beskrywing", - "search_by_description_example": "Stapdag in Sapa" + "search_by_description_example": "Stapdag in Sapa", + "version": "Weergawe", + "version_announcement_closing": "Jou friend, Alex", + "version_history": "Weergawegeskiedenis", + "version_history_item": "{version} geinstaleerd op {date}", + "video": "Video", + "videos": "Video's", + "view": "Bekyk", + "view_album": "Bekyk Album", + "view_all": "Bekyk alle", + "view_all_users": "Bekyk alle gebruikers", + "view_in_timeline": "Bekyk in tydlyn", + "view_link": "Bekyk skakel", + "view_links": "Bekyk skakels", + "view_name": "Bekyk", + "view_next_asset": "Bekyk volgende bate", + "view_previous_asset": "Bekyk vorige bate", + "view_qr_code": "Bekyk QR-kode", + "view_stack": "Bekyk stapel", + "view_user": "Bekyk gebruiker", + "viewer_remove_from_stack": "Verwyder van stapel", + "viewer_stack_use_as_main_asset": "Gebruik as hoofbate", + "viewer_unstack": "Ontstapel", + "visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}", + "waiting": "Wag", + "warning": "Waaskuwing", + "week": "Week", + "welcome": "Welkom", + "welcome_to_immich": "Welkom by Immich", + "wifi_name": "Wi-Fi Naam", + "wrong_pin_code": "Verkeerde PIN-kode", + "year": "Jaar", + "years_ago": "{years, plural, one {# year} other {# years}} gelede", + "yes": "Ja", + "you_dont_have_any_shared_links": "Jy het geen gedeelde skakels", + "your_wifi_name": "Jou Wi-Fi naam", + "zoom_image": "Vergroot Prent" } diff --git a/i18n/ar.json b/i18n/ar.json index 67f7752ae1..6042c8f82f 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -1,19 +1,20 @@ { - "about": "من نحن", - "account": "الحساب", + "about": "عن", + "account": "حساب", "account_settings": "إعدادات الحساب", "acknowledge": "أُدرك ذلك", - "action": "التحكم", + "action": "عملية", "action_common_update": "تحديث", - "actions": "العمليات", + "actions": "عمليات", "active": "نشط", - "activity": "النشاط", + "activity": "نشاط", "activity_changed": "النشاط {enabled, select, true {مُفْعل} other {معطّل}}", "add": "إضافة", "add_a_description": "إضافة وصف", "add_a_location": "إضافة موقع", "add_a_name": "إضافة إسم", "add_a_title": "إضافة عنوان", + "add_endpoint": "اضف نقطة نهاية", "add_exclusion_pattern": "إضافة نمط إستثناء", "add_import_path": "إضافة مسار الإستيراد", "add_location": "إضافة موقع", @@ -21,28 +22,30 @@ "add_partner": "أضف شريكًا", "add_path": "إضافة مسار", "add_photos": "إضافة صور", + "add_tag": "اضف علامة", "add_to": "إضافة إلى…", "add_to_album": "إضافة إلى ألبوم", "add_to_album_bottom_sheet_added": "تمت الاضافة{album}", "add_to_album_bottom_sheet_already_exists": "موجودة مسبقا {album}", - "add_to_shared_album": "إضافة إلى ألبوم مشترك", + "add_to_shared_album": "إضافة إلى ألبوم مشارك", "add_url": "إضافة رابط", "added_to_archive": "أُضيفت للأرشيف", "added_to_favorites": "أُضيفت للمفضلات", "added_to_favorites_count": "تم إضافة {count, number} إلى المفضلات", "admin": { "add_exclusion_pattern_description": "إضافة أنماط الاستبعاد. يدعم التمويه باستخدام *، **، و؟. لتجاهل جميع الملفات في أي دليل يسمى \"Raw\"، استخدم \"**/Raw/**\". لتجاهل جميع الملفات التي تنتهي بـ \".tif\"، استخدم \"**/*.tif\". لتجاهل مسار مطلق، استخدم \"/path/to/ignore/**\".", + "admin_user": "مستخدم مدير", "asset_offline_description": "لم يعد هذا الأصل الخاص بالمكتبة الخارجية موجودًا على القرص وتم نقله إلى سلة المهملات. إذا تم نقل الملف داخل المكتبة، فتحقق من الجدول الزمني الخاص بك لمعرفة الأصل الجديد المقابل. لاستعادة هذا الأصل، يرجى التأكد من إمكانية الوصول إلى مسار الملف أدناه بواسطة Immich ومن ثم قم بمسح المكتبة.", "authentication_settings": "إعدادات المصادقة", "authentication_settings_description": "إدارة كلمة المرور وOAuth وإعدادات المصادقة الأُخرى", "authentication_settings_disable_all": "هل أنت متأكد أنك تريد تعطيل جميع وسائل تسجيل الدخول؟ سيتم تعطيل تسجيل الدخول بالكامل.", "authentication_settings_reenable": "لإعادة التفعيل، استخدم أمر الخادم.", "background_task_job": "المهام الخلفية", - "backup_database": "قاعدة البيانات الاحتياطية", - "backup_database_enable_description": "تمكين النسخ الاحتياطي لقاعدة البيانات", - "backup_keep_last_amount": "مقدار النسخ الاحتياطية السابقة للاحتفاظ بها", - "backup_settings": "إعدادات النسخ الاحتياطي", - "backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات", + "backup_database": "انشاء تفريغ قاعدة البيانات", + "backup_database_enable_description": "تمكين تفريغ قاعدة البيانات", + "backup_keep_last_amount": "مقدار التفريغات السابقة للاحتفاظ بها", + "backup_settings": "إعدادات تفريغ قاعدة البيانات", + "backup_settings_description": "إدارة إعدادات تفريغ قاعدة البيانات.", "cleared_jobs": "تم إخلاء مهام: {job}", "config_set_by_file": "الإعدادات حاليًا معينة عن طريق ملف الاعدادات", "confirm_delete_library": "هل أنت متأكد أنك تريد حذف مكتبة {library}؟", @@ -50,6 +53,7 @@ "confirm_email_below": "للتأكيد، اكتب \"{email}\" بالأسفل", "confirm_reprocess_all_faces": "هل أنت متأكد أنك تريد إعادة معالجة جميع الوجوه؟ سيخلي هذا كل الأشخاص الذين سَميتَهم.", "confirm_user_password_reset": "هل أنت متأكد أنك تريد إعادة تعيين كلمة مرور {user}؟", + "confirm_user_pin_code_reset": "هل انت متاكد من اعادة ضبط رمز PIN الخاص ب {user}؟", "create_job": "إنشاء وظيفة", "cron_expression": "تعبير Cron", "cron_expression_description": "اضبط الفاصل الزمني للفحص باستخدام تنسيق cron. لمزيد من المعلومات يُرجى الرجوع إلى Crontab Guru على سبيل المثال", @@ -65,10 +69,15 @@ "force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.", "image_format": "التنسيق", "image_format_description": "يُنتج WebP ملفات أصغر حجمًا من ملفات JPEG، ولكنه أبطأ في عملية الترميز.", + "image_fullsize_description": "صورة بحجم كامل مع ازالة البيانات الوصفية، تستخدم عند التكبير", + "image_fullsize_enabled": "تمكين توليد الصور بحجم كامل", + "image_fullsize_enabled_description": "توليد صور بحجم كامل للصيغ الغير صديقة للويب. عند تفعيل \"تفضيل العرض المدمج\" ، العروض المدمجه تستخدم بشكل مباشر بدون تحويل. لا يؤثر على الصيغ الصديقة للويب مثل JPEG.", + "image_fullsize_quality_description": "صور بدقة كاملة من ١-١٠٠. الاعلى افضل ولكن ينتج ملفات بحجم اكبر.", + "image_fullsize_title": "اعدادات الصور بحجم كامل", "image_prefer_embedded_preview": "تفضيل المعاينة المدمجة", - "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. يؤدي لإنتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", - "image_prefer_wide_gamut": "تفضيل نطاق الألوان الواسع", - "image_prefer_wide_gamut_setting_description": "استخدم Display P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", + "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. ينتج عنه انتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", + "image_prefer_wide_gamut": "تفضيل تدرج الألوان الواسع", + "image_prefer_wide_gamut_setting_description": "استخدم عرض P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", "image_preview_description": "صورة متوسطة الحجم مع بيانات وصفية مجردة، تُستخدم عند عرض أصل واحد وللتعلم الآلي", "image_preview_quality_description": "جودة المعاينة من 1 إلى 100. كلما كانت القيمة أعلى كان ذلك أفضل، ولكنها تنتج ملفات أكبر وقد تقلل من استجابة التطبيق. قد يؤثر ضبط قيمة منخفضة على جودة التعلم الآلي.", "image_preview_title": "إعدادات المعاينة", @@ -91,18 +100,18 @@ "library_created": "تم إنشاء المكتبة: {library}", "library_deleted": "تم حذف المكتبة", "library_import_path_description": "حدد مجلدًا للاستيراد. سيتم فحص هذا المجلد، بما في ذلك المجلدات الفرعية، بحثًا عن الصور ومقاطع الفيديو.", - "library_scanning": "الفحص الدوري", - "library_scanning_description": "إعداد فحص المكتبة الدوري", - "library_scanning_enable_description": "تفعيل فحص المكتبة الدوري", + "library_scanning": "المسح الدوري", + "library_scanning_description": "إعداد مسح المكتبة الدوري", + "library_scanning_enable_description": "تفعيل مسح المكتبة الدوري", "library_settings": "المكتبة الخارجية", "library_settings_description": "إدارة إعدادات المكتبة الخارجية", "library_tasks_description": "مسح المكتبات الخارجية للعثور على الأصول الجديدة و/أو المتغيرة", - "library_watching_enable_description": "راقب المكتبات الخارجية لتتبع تغييرات الملفات", + "library_watching_enable_description": "راقب المكتبات الخارجية لتغييرات الملفات", "library_watching_settings": "مراقبة المكتبات (تجريبي)", "library_watching_settings_description": "راقب تلقائيًا التغييرات في الملفات", "logging_enable_description": "تفعيل تسجيل الأحداث", "logging_level_description": "عند التفعيل، أي مستوى تسجيل سيستخدم.", - "logging_settings": "تسجيل الأحداث", + "logging_settings": "تسجيل الاحداث", "machine_learning_clip_model": "نموذج CLIP", "machine_learning_clip_model_description": "اسم نموذج CLIP مدرجٌ هنا. يرجى ملاحظة أنه يجب إعادة تشغيل وظيفة \"البحث الذكي\" لجميع الصور بعد تغيير النموذج.", "machine_learning_duplicate_detection": "كشف التكرار", @@ -142,10 +151,10 @@ "map_light_style": "النمط الفاتح", "map_manage_reverse_geocoding_settings": "إدارة إعدادات التكوين الجغرافي المعكوس", "map_reverse_geocoding": "عكس الترميز الجغرافي", - "map_reverse_geocoding_enable_description": "تفعيل عكس الترميز الجغرافي", - "map_reverse_geocoding_settings": "إعدادات عكس الترميز الجغرافي", - "map_settings": "الخريطة", - "map_settings_description": "إدارة إعدادات الخريطة", + "map_reverse_geocoding_enable_description": "تفعيل الترميز الجغرافي العكسي", + "map_reverse_geocoding_settings": "إعدادات الترميز الجغرافي العكسي", + "map_settings": "الخارطة", + "map_settings_description": "إدارة إعدادات الخارطة", "map_style_description": "عنوان URL لسمة الخريطة style.json", "memory_cleanup_job": "تنظيف الذاكرة", "memory_generate_job": "توليد الذاكرة", @@ -157,12 +166,26 @@ "metadata_settings_description": "إدارة إعدادات البيانات الوصفية", "migration_job": "ترحيل", "migration_job_description": "ترحيل الصور المصغرة للمحتويات والوجوه إلى أحدث هيكل مجلدات", + "nightly_tasks_cluster_faces_setting_description": "قم بتشغيل التعرف على الوجه على الوجوه المكتشفة حديثا", + "nightly_tasks_cluster_new_faces_setting": "مجموعة الوجوه الجديدة", + "nightly_tasks_database_cleanup_setting": "مهام تنظيف قاعدة البيانات", + "nightly_tasks_database_cleanup_setting_description": "قم بتنظيف البيانات القديمة منتهية الصلاحية من قاعدة البيانات", + "nightly_tasks_generate_memories_setting": "إنشاء الذكريات", + "nightly_tasks_generate_memories_setting_description": "إنشاء ذكريات جديدة من الأصول", + "nightly_tasks_missing_thumbnails_setting": "إنشاء صور مصغرة مفقودة", + "nightly_tasks_missing_thumbnails_setting_description": "أصول قائمة الانتظار بدون صور مصغرة لإنشاء الصور المصغرة", + "nightly_tasks_settings": "إعدادات المهام الليلية", + "nightly_tasks_settings_description": "إدارة المهام الليلية", + "nightly_tasks_start_time_setting": "وقت البدء", + "nightly_tasks_start_time_setting_description": "الوقت الذي يبدأ فيه الخادم في تشغيل المهام الليلية", + "nightly_tasks_sync_quota_usage_setting": "مزامنة حصة الاستخدام", + "nightly_tasks_sync_quota_usage_setting_description": "تحديث حصة تخزين المستخدم، بناء على الاستخدام الحالي", "no_paths_added": "لم يتم إضافة أي مسارات", "no_pattern_added": "لم يتم إضافة أي أنماط", - "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", + "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق سمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", "note_cannot_be_changed_later": "ملاحظة: لا يمكن تغيير هذا لاحقًا!", "notification_email_from_address": "عنوان المرسل", - "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\"", + "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\". تاكد من استخدام عنوان بريد الكتروني يسمح لك بارسال البريد الالكتروني منه.", "notification_email_host_description": "مضيف خادم البريد الإلكتروني (مثلًا: smtp.immich.app)", "notification_email_ignore_certificate_errors": "تجاهل أخطاء الشهادة", "notification_email_ignore_certificate_errors_description": "تجاهل أخطاء التحقق من صحة شهادة TLS (غير مستحسن)", @@ -182,6 +205,7 @@ "oauth_auto_register": "التسجيل التلقائي", "oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth", "oauth_button_text": "نص الزر", + "oauth_client_secret_description": "مطلوب اذاPKCE(مفتاح الاثبات لتبادل الكود) لم يتم توفيره من مزود OAuth", "oauth_enable_description": "تسجيل الدخول باستخدام OAuth", "oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف", "oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف", @@ -189,12 +213,14 @@ "oauth_settings": "OAuth", "oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth", "oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى الوثائق.", - "oauth_storage_label_claim": "المطالبة بتصنيف التخزين", - "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", + "oauth_storage_label_claim": "المطالبة بسمة التخزين", + "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين سمة التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_claim": "المطالبة بحصة التخزين", "oauth_storage_quota_claim_description": "قم تلقائيًا بتعيين حصة التخزين للمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_default": "حصة التخزين الافتراضية (جيجابايت)", - "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة (أدخل 0 لحصة غير محدودة).", + "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة.", + "oauth_timeout": "نفاذ وقت الطلب", + "oauth_timeout_description": "نفاذ وقت الطلب بالميلي ثانية", "password_enable_description": "تسجيل الدخول باستخدام البريد الكتروني وكلمة المرور", "password_settings": "تسجيل الدخول بكلمة المرور", "password_settings_description": "إدارة تسجيل الدخول بكلمة المرور", @@ -229,13 +255,14 @@ "storage_template_hash_verification_enabled_description": "تفعيل التحقق من الهاش، لا تعطل هذا إلا إذا كنت متأكدًا من تأثيراته", "storage_template_migration": "تهجير قالب التخزين", "storage_template_migration_description": "قم بتطبيق القالب الحالي {template} على المحتويات التي تم رفعها سابقًا", - "storage_template_migration_info": "تغييرات القالب ستنطبق فقط على المحتويات الجديدة. لتطبيق القالب على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", + "storage_template_migration_info": "تغييرات النموذج الخزني ستغير جميع الصيغ الى احرف صغيرة. تغييرات النموذج ستنطبق فقط على المحتويات الجديدة. لتطبيق النموذج على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", "storage_template_migration_job": "وظيفة تهجير قالب التخزين", "storage_template_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى Storage Template وimplications", + "storage_template_onboarding_description_v2": "عند التفعيل. هذه الخاصية ستقوم بالترتيب التلقائي للملفات بناء على نموذج معرف من قبل المستخدم. رجاء اطلع على التوثيق.", "storage_template_path_length": "الحد التقريبي لطول المسار: {length, number}/{limit, number}", "storage_template_settings": "قالب التخزين", "storage_template_settings_description": "إدارة هيكل المجلد واسم الملف للأصول المرفوعة", - "storage_template_user_label": "{label} هو تسمية التخزين الخاصة بالمستخدم", + "storage_template_user_label": "{label} هو سمة التخزين الخاصة بالمستخدم", "system_settings": "إعدادات النظام", "tag_cleanup_job": "تنظيف العلامة", "template_email_available_tags": "يمكنك استخدام المتغيرات التالية في القالب الخاص بك: {tags}", @@ -246,15 +273,15 @@ "template_email_update_album": "تحديث قالب الألبوم", "template_email_welcome": "قالب البريد الإلكتروني الترحيبي", "template_settings": "قوالب الإشعارات", - "template_settings_description": "إدارة القوالب المخصصة للإشعارات.", + "template_settings_description": "إدارة القوالب المخصصة للإشعارات", "theme_custom_css_settings": "CSS مخصص", "theme_custom_css_settings_description": "أوراق الأنماط المتتالية تسمح بتخصيص تصميم Immich.", "theme_settings": "إعدادات السمة", "theme_settings_description": "إدارة تخصيص واجهة ويب Immich", "thumbnail_generation_job": "إنشاء الصور المصغرة", "thumbnail_generation_job_description": "إنشاء صور مصغرة كبيرة وصغيرة وغير واضحة لكل أصل، بالإضافة إلى صور مصغرة لكل شخص", - "transcoding_acceleration_api": "واجهة برمجة التطبيقات للتسريع", - "transcoding_acceleration_api_description": "الواجهة البرمجية التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", + "transcoding_acceleration_api": "تسريع API", + "transcoding_acceleration_api_description": "API التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", "transcoding_acceleration_nvenc": "NVENC (يتطلب GPU من NVIDIA)", "transcoding_acceleration_qsv": "Quick Sync (يتطلب معالج Intel من الجيل السابع أو أحدث)", "transcoding_acceleration_rkmpp": "RKMPP (فقط على شرائح Rockchip SOC)", @@ -278,13 +305,13 @@ "transcoding_encoding_options": "خيارات الترميز", "transcoding_encoding_options_description": "اضبط برامج الترميز والدقة والجودة والخيارات الأخرى لمقاطع الفيديو المشفرة", "transcoding_hardware_acceleration": "التسريع العتادي", - "transcoding_hardware_acceleration_description": "تجريبي؛ أسرع بكثير، ولكن ستكون جودتها أقل عند نفس معدل البت", + "transcoding_hardware_acceleration_description": "تجريبي: ترميز اسرع لكن قد يقلل من الجودة مع معدل بت اقل", "transcoding_hardware_decoding": "فك تشفير الأجهزة", "transcoding_hardware_decoding_setting_description": "ينطبق ذلك فقط على NVENC، QSV، و RKMPP. يمكن التسريع من طرف لطرف بدلاً من تسريع الترميز فقط. قد لا يعمل على جميع مقاطع الفيديو.", "transcoding_max_b_frames": "أقصى عدد من الإطارات B", "transcoding_max_b_frames_description": "القيم الأعلى تعزز كفاءة الضغط، ولكنها تبطئ عملية الترميز. قد لا تكون متوافقة مع التسريع العتادي على الأجهزة القديمة. قيمة 0 تعطل إطارات B، بينما تضبط القيمة -1 هذا القيمة تلقائيًا.", "transcoding_max_bitrate": "الحد الأقصى لمعدل البت", - "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بايت لـ VP9 أو HEVC، أو 4500 كيلو بايت لـ H.264. معطل إذا تم ضبطه على 0.", + "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بت لـ VP9 أو HEVC، أو 4500 كيلو بت لـ H.264. معطل إذا تم ضبطه على 0.", "transcoding_max_keyframe_interval": "الحد الأقصى للفاصل الزمني للإطار الرئيسي", "transcoding_max_keyframe_interval_description": "يضبط الحد الأقصى لمسافة الإطار بين الإطارات الرئيسية. تؤدي القيم المنخفضة إلى زيادة سوء كفاءة الضغط، ولكنها تعمل على تحسين أوقات البحث وقد تعمل على تحسين الجودة في المشاهد ذات الحركة السريعة. 0 يضبط هذه القيمة تلقائيًا.", "transcoding_optimal_description": "مقاطع الفيديو ذات الدقة الأعلى من الدقة المستهدفة أو بتنسيق غير مقبول", @@ -324,6 +351,7 @@ "user_delete_delay_settings_description": "عدد الأيام بعد الإزالة لحذف حساب المستخدم ومحتوياته بشكل دائم. تقوم وظيفة حذف المستخدم بالتشغيل في منتصف الليل للتحقق من المستخدمين الجاهزين للحذف. سيتم تقييم التغييرات على هذا الإعداد في التنفيذ القادم.", "user_delete_immediately": "سيتم وضع حساب {user} ومحتوياته في قائمة الانتظار للحذف الدائم على الفور.", "user_delete_immediately_checkbox": "قائمة انتظار المستخدم والمحتويات للحذف الفوري", + "user_details": "تفاصيل المستخدم", "user_management": "إدارة المستخدم", "user_password_has_been_reset": "تمت إعادة تعيين كلمة المرور الخاصة بالمستخدم:", "user_password_reset_description": "يرجى تزويد المستخدم بكلمة المرور المؤقتة وإبلاغه بأنه سيحتاج إلى تغيير كلمة المرور عند تسجيل الدخول التالي.", @@ -343,9 +371,17 @@ "admin_password": "كلمة سر المشرف", "administration": "الإدارة", "advanced": "متقدم", + "advanced_settings_enable_alternate_media_filter_subtitle": "استخدم هذا الخيار لتصفية الوسائط اثناء المزامنه بناء على معايير بديلة. جرب هذا الخيار فقط كان لديك مشاكل مع التطبيق بالكشف عن جميع الالبومات.", + "advanced_settings_enable_alternate_media_filter_title": "[تجريبي] استخدم جهاز تصفية مزامنه البومات بديل", + "advanced_settings_log_level_title": "مستوى السجل: {level}", "advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.", "advanced_settings_prefer_remote_title": "تفضل الصور البعيدة", + "advanced_settings_proxy_headers_subtitle": "عرف عناوين الوكيل التي يستخدمها Immich لارسال كل طلب شبكي", + "advanced_settings_proxy_headers_title": "عناوين الوكيل", + "advanced_settings_self_signed_ssl_subtitle": "تخطي التحقق من شهادة SSL لخادم النقطة النهائي. مكلوب للشهادات الموقعة ذاتيا.", "advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا", + "advanced_settings_sync_remote_deletions_subtitle": "حذف او استعادة تلقائي للاصول على هذا الجهاز عند تنفيذ العملية على الويب", + "advanced_settings_sync_remote_deletions_title": "مزامنة عمليات الحذف عن بعد [تجريبي]", "advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة", "advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها", "advanced_settings_troubleshooting_title": "استكشاف الأخطاء وإصلاحها", @@ -382,6 +418,9 @@ "album_with_link_access": "السماح لأي شخص لديه الرابط برؤية الصور والأشخاص الموجودين في هذا الألبوم.", "albums": "الألبومات", "albums_count": "{count, plural, one {{count, number} ألبوم} other {{count, number} ألبومات}}", + "albums_default_sort_order": "ترتيب الألبوم الافتراضي", + "albums_default_sort_order_description": "ترتيب فرز الأصول الأولي عند إنشاء ألبومات جديدة.", + "albums_feature_description": "مجموعة من الأصول التي يمكن مشاركتها مع مستخدمين آخرين.", "all": "الكل", "all_albums": "جميع الألبومات", "all_people": "جميع الأشخاص", @@ -392,20 +431,23 @@ "allow_public_user_to_upload": "السماح للمستخدم العام بالرفع", "alt_text_qr_code": "صورة رمز الاستجابة السريعة (QR)", "anti_clockwise": "عكس اتجاه عقارب الساعة", - "api_key": "مفتاح واجهة برمجة التطبيقات", + "api_key": "مفتاح API", "api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.", "api_key_empty": "يجب ألا يكون اسم مفتاح API فارغًا", - "api_keys": "مفاتيح واجهة برمجة التطبيقات", + "api_keys": "مفاتيح API", "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد الخروج", "app_bar_signout_dialog_ok": "نعم", "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", "appears_in": "يظهر في", "archive": "الأرشيف", + "archive_action_prompt": "{count} اضيف إلى الارشيف", "archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها", "archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة", + "archive_page_title": "ارشيف ({count})", "archive_size": "حجم الأرشيف", "archive_size_description": "تكوين حجم الأرشيف للتنزيلات (بالجيجابايت)", + "archived": "مؤرشفة", "archived_count": "{count, plural, other {الأرشيف #}}", "are_these_the_same_person": "هل هؤلاء هم نفس الشخص؟", "are_you_sure_to_do_this": "هل انت متأكد من أنك تريد أن تفعل هذا؟", @@ -427,37 +469,55 @@ "asset_list_settings_title": "شبكة الصور", "asset_offline": "المحتوى غير اتصال", "asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.", + "asset_restored_successfully": "تم استعادة الاصل بنجاح", "asset_skipped": "تم تخطيه", "asset_skipped_in_trash": "في سلة المهملات", "asset_uploaded": "تم الرفع", "asset_uploading": "جارٍ الرفع…", + "asset_viewer_settings_subtitle": "إدارة إعدادات عارض المعرض الخاص بك", "asset_viewer_settings_title": "عارض الأصول", "assets": "المحتويات", "assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}", "assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم", - "assets_added_to_name_count": "تم إضافة {count, plural, one {# محتوى} other {# محتويات }} إلى {hasName, select, true {{name}} other {ألبوم جديد}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} لايمكن اضافته الى الالبوم", "assets_count": "{count, plural, one {# محتوى} other {# محتويات}}", + "assets_deleted_permanently": "{count} الاص(و)ل المحذوف(ه) بشكل دائم", + "assets_deleted_permanently_from_server": "{count} الاص(و)ل المحذوف(ه) بشكل دائمي من خادم Immich", + "assets_downloaded_failed": "{count, plural, one {تم التحميل # ملف - {error} ملف فشل} other {تم التحميل # ملفات - {error} ملفات فشلت}}", + "assets_downloaded_successfully": "{count, plural, one {تم التحميل # ملف بنجاح} other {تم التحميل # ملفات بنجاح}}", "assets_moved_to_trash_count": "تم نقل {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", "assets_permanently_deleted_count": "تم حذف {count, plural, one {# هذا المحتوى} other {# هذه المحتويات}} بشكل دائم", "assets_removed_count": "تمت إزالة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_removed_permanently_from_device": "{count} الاص(و)ل محذوف(ه) من الجهاز", "assets_restore_confirmation": "هل أنت متأكد من أنك تريد استعادة جميع الأصول المحذوفة؟ لا يمكنك التراجع عن هذا الإجراء! لاحظ أنه لا يمكن استعادة أي أصول غير متصلة بهذه الطريقة.", "assets_restored_count": "تمت استعادة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_restored_successfully": "{count} الاص(و)ل المستعاد(ه) بنجاح", + "assets_trashed": "{count} الاصل(و) ل المنقوله الى سلة المهملات", "assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", + "assets_trashed_from_server": "{count} الاص(و)ل المنقولة الى سلة المهملات من خادم Immich", "assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل", "authorized_devices": "الأجهزه المخولة", + "automatic_endpoint_switching_subtitle": "اتصل محليا من خلال شبكه Wi-Fi عند توفرها و استخدم اتصالات بديله في الاماكن الاخرى", + "automatic_endpoint_switching_title": "تبديل URL تلقائي", + "autoplay_slideshow": "تشغيل تلقائي لعرض الشرائح", "back": "خلف", "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", + "background_location_permission": "اذن الوصول للموقع في الخلفية", + "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", "backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.", "backup_album_selection_page_select_albums": "حدد الألبومات", "backup_album_selection_page_selection_info": "معلومات الاختيار", "backup_album_selection_page_total_assets": "إجمالي الأصول الفريدة", "backup_all": "الجميع", - "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...", - "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...", - "backup_background_service_default_notification": "التحقق من الأصول الجديدة ...", + "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة…", + "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة…", + "backup_background_service_current_upload_notification": "تحميل {filename}", + "backup_background_service_default_notification": "التحقق من الأصول الجديدة…", "backup_background_service_error_title": "خطأ في النسخ الاحتياطي", - "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...", + "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك…", + "backup_background_service_upload_failure_notification": "فشل تحميل {filename}", "backup_controller_page_albums": "ألبومات احتياطية", "backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.", "backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية", @@ -468,17 +528,22 @@ "backup_controller_page_background_battery_info_title": "تحسين البطارية", "backup_controller_page_background_charging": "فقط أثناء الشحن", "backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية", + "backup_controller_page_background_delay": "تاخير الخزن التلقائي للاصول: {duration}", "backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق", "backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية", "backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل", "backup_controller_page_background_turn_off": "قم بإيقاف تشغيل خدمة الخلفية", "backup_controller_page_background_turn_on": "قم بتشغيل خدمة الخلفية", - "backup_controller_page_background_wifi": "فقط على واي فاي", + "backup_controller_page_background_wifi": "فقط على Wi-Fi", "backup_controller_page_backup": "دعم", "backup_controller_page_backup_selected": "المحدد: ", "backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو", + "backup_controller_page_created": "انشئ في :{date}", "backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.", "backup_controller_page_excluded": "مستبعد: ", + "backup_controller_page_failed": "فشل ({count})", + "backup_controller_page_filename": "اسم الملف : {filename} [{size}]", + "backup_controller_page_id": "هوية: {id}", "backup_controller_page_info": "معلومات النسخ الاحتياطي", "backup_controller_page_none_selected": "لم يتم التحديد", "backup_controller_page_remainder": "بقية", @@ -487,7 +552,8 @@ "backup_controller_page_start_backup": "بدء النسخ الاحتياطي", "backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة", "backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة", - "backup_controller_page_to_backup": "الألبومات الاحتياطية", + "backup_controller_page_storage_format": "{used} من {total} مستخدم", + "backup_controller_page_to_backup": "الألبومات التي سيتم نسخها احتياطيا", "backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة", "backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة", "backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة", @@ -499,7 +565,12 @@ "backup_manual_success": "نجاح", "backup_manual_title": "حالة التحميل", "backup_options_page_title": "خيارات النسخ الاحتياطي", + "backup_setting_subtitle": "ادارة اعدادات التحميل في الخلفية والمقدمة", "backward": "الى الوراء", + "biometric_auth_enabled": "المصادقة البايومترية مفعله", + "biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية", + "biometric_no_options": "لا توجد خيارات بايومترية متوفرة", + "biometric_not_available": "االمصادقة البيومترية غير متاحة على هذا الجهاز", "birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح", "birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.", "blurred_background": "خلفية مشوشة", @@ -514,12 +585,13 @@ "cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.", "cache_settings_duplicated_assets_clear_button": "واضح", "cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق", + "cache_settings_duplicated_assets_title": "الاصول المكررة ({count})", "cache_settings_statistics_album": "مكتبه الصور المصغره", "cache_settings_statistics_full": "صور كاملة", "cache_settings_statistics_shared": "صورة ألبوم مشتركة", "cache_settings_statistics_thumbnail": "الصورة المصغرة", "cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت", - "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.", + "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق Immich الجوال", "cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي", "cache_settings_tile_title": "التخزين المحلي", "cache_settings_title": "إعدادات التخزين المؤقت", @@ -527,11 +599,16 @@ "camera_brand": "علامة الكاميرا التجارية", "camera_model": "طراز الكاميرا", "cancel": "إلغاء", - "cancel_search": "الغي البحث", + "cancel_search": "الغاء البحث", + "canceled": "تم الالغاء", "cannot_merge_people": "لا يمكن دمج الأشخاص", "cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!", "cannot_update_the_description": "لا يمكن تحديث الوصف", + "cast": "بث", + "cast_description": "ضبط وجهات البث المتوفرة", "change_date": "غيّر التاريخ", + "change_description": "تغيير الوصف", + "change_display_order": "تغيير ترتيب العرض", "change_expiration_time": "تغيير وقت انتهاء الصلاحية", "change_location": "غيّر الموقع", "change_name": "تغيير الإسم", @@ -543,9 +620,12 @@ "change_password_form_new_password": "كلمة المرور الجديدة", "change_password_form_password_mismatch": "كلمة المرور غير مطابقة", "change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة", - "change_pin_code": "تغيير الرقم السري", + "change_pin_code": "تغيير رمز PIN", "change_your_password": "غير كلمة المرور الخاصة بك", "changed_visibility_successfully": "تم تغيير الرؤية بنجاح", + "check_corrupt_asset_backup": "التحقق من وجود نسخ احتياطية فاسدة للاصول", + "check_corrupt_asset_backup_button": "اجراء فحص", + "check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.", "check_logs": "تحقق من السجلات", "choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم", "city": "المدينة", @@ -554,6 +634,14 @@ "clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة", "clear_message": "إخلاء الرسالة", "clear_value": "إخلاء القيمة", + "client_cert_dialog_msg_confirm": "حسنا", + "client_cert_enter_password": "ادخل كلمة سر", + "client_cert_import": "استيراد", + "client_cert_import_success_msg": "تم استيراد شهادة العميل", + "client_cert_invalid_msg": "ملف شهادة عميل غير صالحة او كلمة سر غير صحيحة", + "client_cert_remove_msg": "تم ازالة شهادة العميل", + "client_cert_subtitle": "يدعم صيغ PKCS12 (.p12, .pfx)فقط. استيراد/ازالة الشهادات متاح فقط قبل تسجيل الدخول", + "client_cert_title": "شهادة مستخدم SSL", "clockwise": "باتجاه عقارب الساعة", "close": "إغلاق", "collapse": "طي", @@ -566,21 +654,27 @@ "comments_are_disabled": "التعليقات معطلة", "common_create_new_album": "إنشاء ألبوم جديد", "common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.", + "completed": "اكتمل", "confirm": "تأكيد", "confirm_admin_password": "تأكيد كلمة مرور المسؤول", "confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟", "confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟", "confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟", - "confirm_new_pin_code": "ثبت الرقم السري الجديد", + "confirm_new_pin_code": "ثبت رمز PIN الجديد", "confirm_password": "تأكيد كلمة المرور", + "confirm_tag_face": "هل تريد وضع علامة على هذا الوجه {name}؟", + "confirm_tag_face_unnamed": "هل تريد وضع علامة على هذا الوجه؟", + "connected_device": "جهاز متصل", + "connected_to": "متصل ب", "contain": "محتواة", "context": "السياق", "continue": "متابعة", "control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد", - "control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق", + "control_bottom_app_bar_delete_from_immich": "حذف من Immich", "control_bottom_app_bar_delete_from_local": "حذف من الجهاز", "control_bottom_app_bar_edit_location": "تحديد الوجهة", "control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت", + "control_bottom_app_bar_share_link": "مشاركة رابط", "control_bottom_app_bar_share_to": "مشاركة إلى", "control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات", "copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.", @@ -602,6 +696,7 @@ "create_link": "إنشاء رابط", "create_link_to_share": "إنشاء رابط للمشاركة", "create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة", + "create_new": "انشاء جديد", "create_new_person": "إنشاء شخص جديد", "create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد", "create_new_user": "إنشاء مستخدم جديد", @@ -611,14 +706,18 @@ "create_tag_description": "أنشئ علامة جديدة. بالنسبة للعلامات المتداخلة، يرجى إدخال المسار الكامل للعلامة بما في ذلك الخطوط المائلة للأمام.", "create_user": "إنشاء مستخدم", "created": "تم الإنشاء", + "created_at": "مخلوق", + "crop": "قص", "curated_object_page_title": "أشياء", "current_device": "الجهاز الحالي", - "current_pin_code": "الرقم السري الحالي", + "current_pin_code": "رمز PIN الحالي", + "current_server_address": "عنوان الخادم الحالي", "custom_locale": "لغة مخصصة", "custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة", "daily_title_text_date": "E ، MMM DD", "daily_title_text_date_year": "E ، MMM DD ، yyyy", "dark": "معتم", + "dark_theme": "تبديل المظهر الداكن", "date_after": "التارخ بعد", "date_and_time": "التاريخ و الوقت", "date_before": "التاريخ قبل", @@ -634,11 +733,12 @@ "default_locale": "اللغة الافتراضية", "default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك", "delete": "حذف", + "delete_action_prompt": "{count} حذف بشكل نهائي", "delete_album": "حذف الألبوم", "delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", - "delete_dialog_alert": " هذه العناصر سيتم حذفها بشكل دائم من جهازك ومن تطبيق", - "delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق", - "delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ", + "delete_dialog_alert": "هذه العناصر سيتم حذفها بشكل دائم من Immich و من جهازك", + "delete_dialog_alert_local": "العناصر التي سيتم حذفها من جهازك ولكن تبقى موجوده في خادم Immich", + "delete_dialog_alert_local_non_backed_up": "بعض العناصر غير مدعومة بنسخة احتياطية على Immich وسيتم إزالتها نهائيًا من جهازك", "delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق", "delete_dialog_ok_force": "احذف على أي حال", "delete_dialog_title": "الحذف بشكل نهائي", @@ -664,7 +764,9 @@ "direction": "الإتجاه", "disabled": "معطل", "disallow_edits": "منع التعديلات", + "discord": "دسكورد", "discover": "اكتشف", + "discovered_devices": "اجهزة مكتشفة", "dismiss_all_errors": "تجاهل كافة الأخطاء", "dismiss_error": "تجاهل الخطأ", "display_options": "عرض الخيارات", @@ -675,12 +777,25 @@ "documentation": "الوثائق", "done": "تم", "download": "تنزيل", + "download_canceled": "الغي التنزيل", + "download_complete": "اكتمل التنزيل", + "download_enqueue": "تنزيل في قائمة الانتظار", + "download_error": "خطا في التنزيل", + "download_failed": "فشل التنزيل", + "download_finished": "انتهى التنزيل", "download_include_embedded_motion_videos": "مقاطع الفيديو المدمجة", "download_include_embedded_motion_videos_description": "تضمين مقاطع الفيديو المضمنة في الصور المتحركة كملف منفصل", + "download_notfound": "لم يعثر على التنزيل", + "download_paused": "اوقف التنزيل", "download_settings": "التنزيلات", "download_settings_description": "إدارة الإعدادات المتعلقة بتنزيل المحتويات", + "download_started": "بدا التنزيل", + "download_sucess": "نجح التنزيل", + "download_sucess_android": "تم تحميل الوسائط الى DCIM/Immich", + "download_waiting_to_retry": "الانتظار للمحاولة", "downloading": "جارٍ التنزيل", "downloading_asset_filename": "{filename} قيد التنزيل", + "downloading_media": "تحميل الوسائط", "drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها", "duplicates": "التكرارات", "duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت", @@ -690,6 +805,8 @@ "edit_avatar": "تعديل الصورة الشخصية", "edit_date": "تعديل التاريخ", "edit_date_and_time": "تعديل التاريخ والوقت", + "edit_description": "تعديل الوصف", + "edit_description_prompt": "الرجاء اختيار وصف جديد:", "edit_exclusion_pattern": "تعديل نمط الاستبعاد", "edit_faces": "تعديل الوجوه", "edit_import_path": "تعديل مسار الاستيراد", @@ -697,6 +814,7 @@ "edit_key": "تعديل المفتاح", "edit_link": "تغيير الرابط", "edit_location": "تعديل الموقع", + "edit_location_action_prompt": "{count} موقع تم تعديله", "edit_location_dialog_title": "موقع", "edit_name": "تعديل الاسم", "edit_people": "تعديل الأشخاص", @@ -710,15 +828,24 @@ "editor_crop_tool_h2_aspect_ratios": "نسب العرض إلى الارتفاع", "editor_crop_tool_h2_rotation": "التدوير", "email": "البريد الإلكتروني", + "email_notifications": "تنبيهات البريد الالكتروني", + "empty_folder": "هذا المجلد فارغ", "empty_trash": "أفرغ سلة المهملات", "empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!", "enable": "تفعيل", + "enable_biometric_auth_description": "أدخل رمز PIN الخاص بك لتمكين المصادقة البيومترية", "enabled": "مفعل", "end_date": "تاريخ الإنتهاء", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "مُدرج في الطابور", + "enter_wifi_name": "ادخل اسم Wi-Fi", + "enter_your_pin_code": "أدخل رمز PIN الخاص بك", + "enter_your_pin_code_subtitle": "أدخل رمز PIN الخاص بك للوصول إلى المجلد المقفل", "error": "خطأ", + "error_change_sort_album": "فشل في تغيير ترتيب الألبوم", "error_delete_face": "حدث خطأ في حذف الوجه من الأصول", "error_loading_image": "حدث خطأ أثناء تحميل الصورة", + "error_saving_image": "خطأ: {error}", + "error_tag_face_bounding_box": "خطأ في وضع علامة على الوجه - لا يمكن الحصول على إحداثيات المربع المحيط", "error_title": "خطأ - حدث خللٌ ما", "errors": { "cannot_navigate_next_asset": "لا يمكن الانتقال إلى المحتوى التالي", @@ -746,10 +873,12 @@ "failed_to_keep_this_delete_others": "فشل في الاحتفاظ بهذا الأصل وحذف الأصول الأخرى", "failed_to_load_asset": "فشل تحميل المحتوى", "failed_to_load_assets": "فشل تحميل المحتويات", + "failed_to_load_notifications": "فشل تحميل الإشعارات", "failed_to_load_people": "فشل تحميل الأشخاص", "failed_to_remove_product_key": "تعذر إزالة مفتاح المنتج", "failed_to_stack_assets": "فشل في تكديس المحتويات", "failed_to_unstack_assets": "فشل في فصل المحتويات", + "failed_to_update_notification_status": "فشل في تحديث حالة الإشعار", "import_path_already_exists": "مسار الاستيراد هذا موجود مسبقًا.", "incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة", "paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}", @@ -766,6 +895,7 @@ "unable_to_archive_unarchive": "تعذر {archived, select, true {الأرشفة} other {الإخراج من الأرشيف}}", "unable_to_change_album_user_role": "غير قادر على تغيير دور مستخدم الألبوم", "unable_to_change_date": "غير قادر على تغيير التاريخ", + "unable_to_change_description": "غير قادر على تغيير الوصف", "unable_to_change_favorite": "غير قادر على تغيير المفضلة لمحتوى", "unable_to_change_location": "غير قادر على تغيير الموقع", "unable_to_change_password": "غير قادر على تغيير كلمة المرور", @@ -809,6 +939,7 @@ "unable_to_remove_partner": "غير قادر على إزالة الشريك", "unable_to_remove_reaction": "غير قادر على إزالة رد الفعل", "unable_to_reset_password": "غير قادر على إعادة تعيين كلمة المرور", + "unable_to_reset_pin_code": "غير قادر على إعادة تعيين رمز PIN", "unable_to_resolve_duplicate": "غير قادر على حل التكرارات", "unable_to_restore_assets": "غير قادر على استعادة المحتويات", "unable_to_restore_trash": "غير قادر على استعادة سلة المهملات", @@ -842,6 +973,9 @@ "exif_bottom_sheet_location": "موقع", "exif_bottom_sheet_people": "الناس", "exif_bottom_sheet_person_add_person": "اضف اسما", + "exif_bottom_sheet_person_age_months": "العمر {months} اشهر", + "exif_bottom_sheet_person_age_year_months": "العمر ١ سنة،{months} اشهر", + "exif_bottom_sheet_person_age_years": "العمر {years}", "exit_slideshow": "خروج من العرض التقديمي", "expand_all": "توسيع الكل", "experimental_settings_new_asset_list_subtitle": "أعمال جارية", @@ -858,10 +992,15 @@ "extension": "الإمتداد", "external": "خارجي", "external_libraries": "المكتبات الخارجية", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "شبكة خارجية", + "external_network_sheet_info": "عندما لا يتواجد على شبكة Wi-Fi المفضلة، فإنه سيتصل بالخادم من خلال أول عناوين URL أدناه التي يمكنه الوصول إليها، بدءًا من الأعلى إلى الأسفل", "face_unassigned": "غير معين", + "failed": "فشل", + "failed_to_authenticate": "فشل في المصادقة", "failed_to_load_assets": "فشل تحميل الأصول", + "failed_to_load_folder": "فشل تحميل المجلد", "favorite": "مفضل", + "favorite_action_prompt": "{count} اضيف إلى المفضلات", "favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة", "favorites": "المفضلة", "favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة", @@ -872,18 +1011,26 @@ "file_name_or_extension": "اسم الملف أو امتداده", "filename": "اسم الملف", "filetype": "نوع الملف", + "filter": "تصفية", "filter_people": "تصفية الاشخاص", + "filter_places": "تصفية الاماكن", "find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث", "fix_incorrect_match": "إصلاح المطابقة غير الصحيحة", + "folder": "مجلد", + "folder_not_found": "لم يتم العثور على المجلد", "folders": "المجلدات", "folders_feature_description": "تصفح عرض المجلد للصور ومقاطع الفيديو الموجودة على نظام الملفات", "forward": "إلى الأمام", + "gcast_enabled": "كوكل كاست", + "gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.", "general": "عام", "get_help": "الحصول على المساعدة", + "get_wifiname_error": "تعذر الحصول على اسم شبكة Wi-Fi. تأكد من منح الأذونات اللازمة واتصالك بشبكة Wi-Fi", "getting_started": "البدء", "go_back": "الرجوع للخلف", "go_to_folder": "اذهب إلى المجلد", "go_to_search": "اذهب إلى البحث", + "grant_permission": "منح الاذن", "group_albums_by": "تجميع الألبومات حسب...", "group_country": "مجموعة البلد", "group_no": "بدون تجميع", @@ -893,6 +1040,12 @@ "haptic_feedback_switch": "تمكين ردود الفعل اللمسية", "haptic_feedback_title": "ردود فعل لمسية", "has_quota": "محدد بحصة", + "header_settings_add_header_tip": "اضاف راس", + "header_settings_field_validator_msg": "القيمة لا يمكن ان تكون فارغة", + "header_settings_header_name_input": "اسم الرأس", + "header_settings_header_value_input": "قيمة الرأس", + "headers_settings_tile_subtitle": "قم بتعريف رؤوس الوكيل التي يجب أن يرسلها التطبيق مع كل طلب شبكة", + "headers_settings_tile_title": "رؤوس وكيل مخصصة", "hi_user": "مرحبا {name} ({email})", "hide_all_people": "إخفاء جميع الأشخاص", "hide_gallery": "اخفاء المعرض", @@ -911,11 +1064,16 @@ "home_page_delete_remote_err_local": "الأصول المحلية في التحديد البعيد المحذوف، سوف يتخطى", "home_page_favorite_err_local": "لا يمكن تفضيل الأصول المحلية بعد، سوف يتخطى", "home_page_favorite_err_partner": "لا يمكن الأصول الشريكة المفضلة بعد ، سوف يتخطى", - "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو في الألبوم (الألبومات).", + "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو فيه", + "home_page_locked_error_local": "لا يمكن نقل الأصول المحلية إلى المجلد المقفل، يتم التخطي", + "home_page_locked_error_partner": "لا يمكن نقل أصول الشريك إلى المجلد المقفل، يتم التخطي", "home_page_share_err_local": "لا يمكن مشاركة الأصول المحلية عبر الرابط ، سوف يتخطى", "home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى", "host": "المضيف", "hour": "ساعة", + "id": "المعرف", + "ignore_icloud_photos": "تجاهل صور iCloud", + "ignore_icloud_photos_description": "الصور المخزنة في Cloud لن يتم تحميلها إلى خادم Immich", "image": "صورة", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} تم التقاطها مع {person1} في {date}", @@ -927,6 +1085,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}", + "image_saved_successfully": "الصور حُفظت", "image_viewer_page_state_provider_download_started": "بدأ التنزيل", "image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح", "image_viewer_page_state_provider_share_error": "خطأ في المشاركة", @@ -948,8 +1107,16 @@ "night_at_midnight": "كل ليلة عند منتصف الليل", "night_at_twoam": "كل ليلة الساعة 2 صباحا" }, + "invalid_date": "تاريخ غير صالح", + "invalid_date_format": "صيغة تاريخ غير صالحة", "invite_people": "دعوة الأشخاص", "invite_to_album": "دعوة إلى الألبوم", + "ios_debug_info_fetch_ran_at": "جرت عملية الجلب في {dateTime}", + "ios_debug_info_last_sync_at": "اخر مزامنة {dateTime}", + "ios_debug_info_no_processes_queued": "لا توجد عمليات خلفية في قائمة الانتظار", + "ios_debug_info_no_sync_yet": "لم يتم تشغيل أي مهمة مزامنة في الخلفية حتى الآن", + "ios_debug_info_processes_queued": "{count, plural, one {{count} عملية خلفية ادخلتةفي طابور} other {{count} عمليات خلفية ادخلت في طابور}}", + "ios_debug_info_processing_ran_at": "المعالجة جرت في {dateTime}", "items_count": "{count, plural, one {# عنصر} other {# عناصر}}", "jobs": "الوظائف", "keep": "احتفظ", @@ -958,6 +1125,9 @@ "kept_this_deleted_others": "تم الاحتفاظ بهذا الأصل وحذف {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "اختصارات لوحة المفاتيح", "language": "اللغة", + "language_no_results_subtitle": "حاول تعديل مصطلح البحث", + "language_no_results_title": "لم يتم العثور على لغات", + "language_search_hint": "البحث عن لغات...", "language_setting_description": "اختر لغتك المفضلة", "last_seen": "اخر ظهور", "latest_version": "احدث اصدار", @@ -983,21 +1153,29 @@ "list": "قائمة", "loading": "تحميل", "loading_search_results_failed": "فشل تحميل نتائج البحث", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "غير قادر على بث أصل لم يتم تحميله إلى الخادم", + "local_network": "شبكة محلية", + "local_network_sheet_info": "سيتصل التطبيق بالخادم من خلال عنوان URL هذا عند استخدام شبكة Wi-Fi المحددة", + "location_permission": "اذن الموقع", + "location_permission_content": "من أجل استخدام ميزة التبديل التلقائي، يحتاج Immich إلى إذن موقع دقيق حتى يتمكن من قراءة اسم شبكة Wi-Fi الحالية", "location_picker_choose_on_map": "اختر على الخريطة", "location_picker_latitude_error": "أدخل خط عرض صالح", "location_picker_latitude_hint": "أدخل خط العرض الخاص بك هنا", "location_picker_longitude_error": "أدخل خط الطول الصحيح", "location_picker_longitude_hint": "أدخل خط الطول هنا", + "lock": "قفل", + "locked_folder": "مجلد مقفول", "log_out": "تسجيل خروج", "log_out_all_devices": "تسجيل الخروج من كافة الأجهزة", + "logged_in_as": "تم تسجيل الدخول باسم {user}", "logged_out_all_devices": "تم تسجيل الخروج من جميع الأجهزة", "logged_out_device": "تم تسجيل الخروج من الجهاز", "login": "تسجيل الدخول", "login_disabled": "تم تعطيل تسجيل الدخول", - "login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ", + "login_form_api_exception": "استثناء API. يرجى التحقق من عنوان URL الخادم والمحاولة مرة أخرى.", "login_form_back_button_text": "الرجوع للخلف", "login_form_email_hint": "yoursemail@email.com", + "login_form_endpoint_hint": "http://المنفذ:عنوان‫-ip-الخادم", "login_form_endpoint_url": "url نقطة نهاية الخادم", "login_form_err_http": "يرجى تحديد http:// أو https://", "login_form_err_invalid_email": "بريد إلكتروني خاطئ", @@ -1021,7 +1199,8 @@ "look": "الشكل", "loop_videos": "تكرار مقاطع الفيديو", "loop_videos_description": "فَعْل لتكرار مقطع فيديو تلقائيًا في عارض التفاصيل.", - "main_branch_warning": "أنت تستخدم إصداراً تطويرياً؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_branch_warning": "أنت تستخدم إصداراً قيد التطوير؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_menu": "القائمة الرئيسية", "make": "صنع", "manage_shared_links": "إدارة الروابط المشتركة", "manage_sharing_with_partners": "إدارة المشاركة مع الشركاء", @@ -1031,6 +1210,8 @@ "manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها", "manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك", "map": "الخريطة", + "map_assets_in_bound": "{count} صوره", + "map_assets_in_bounds": "{count} صور", "map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم", "map_location_dialog_yes": "نعم", "map_location_picker_page_use_location": "استخدم هذا الموقع", @@ -1044,13 +1225,18 @@ "map_settings": "إعدادات الخريطة", "map_settings_dark_mode": "الوضع المظلم", "map_settings_date_range_option_day": "24 ساعة الماضية", + "map_settings_date_range_option_days": "الايام {days} الماضية", "map_settings_date_range_option_year": "السنة الفائتة", + "map_settings_date_range_option_years": "السنوات {years} الماضية", "map_settings_dialog_title": "إعدادات الخريطة", "map_settings_include_show_archived": "تشمل الأرشفة", "map_settings_include_show_partners": "تضمين الشركاء", "map_settings_only_show_favorites": "اظهار المفضلة فقط", "map_settings_theme_settings": "مظهر الخريطة", "map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور", + "mark_all_as_read": "تحديد الكل كمقروء", + "mark_as_read": "تحديد كمقروء", + "marked_all_as_read": "تم تحديد الكل كمقروء", "matches": "تطابقات", "media_type": "نوع الوسائط", "memories": "الذكريات", @@ -1075,6 +1261,13 @@ "month": "شهر", "monthly_title_text_date_format": "ط ط ط", "more": "المزيد", + "move": "تحريك", + "move_off_locked_folder": "تحريك خارج المجلد المقفل", + "move_to_lock_folder_action_prompt": "{count} اضيف إلى المجلد المقفل", + "move_to_locked_folder": "النقل الى مجلد مغلق", + "move_to_locked_folder_confirmation": "هذه الصور والفديوات ستتم ازالتها من جميع الالبومات، ويمكنان تتم مشاهدتها فقط من خلال المجلد المقفل", + "moved_to_archive": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى الارشيف", + "moved_to_library": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى المكتبة", "moved_to_trash": "تم النقل إلى سلة المهملات", "multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى", "multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى", @@ -1082,12 +1275,15 @@ "my_albums": "ألبوماتي", "name": "الاسم", "name_or_nickname": "الاسم أو اللقب", + "networking_settings": "الشبكات", + "networking_subtitle": "إدارة إعدادات نقطة الخادم النهائية", "never": "أبداً", "new_album": "البوم جديد", "new_api_key": "مفتاح API جديد", "new_password": "كلمة المرور الجديدة", "new_person": "شخص جديد", - "new_pin_code": "الرقم السري الجديد", + "new_pin_code": "رمز PIN الجديد", + "new_pin_code_subtitle": "هذه أول مرة تدخل فيها إلى المجلد المقفل. أنشئ رمزًا PIN للوصول بامان إلى هذه الصفحة", "new_user_created": "تم إنشاء مستخدم جديد", "new_version_available": "إصدار جديد متاح", "newest_first": "الأحدث أولاً", @@ -1100,19 +1296,25 @@ "no_archived_assets_message": "أرشفة الصور ومقاطع الفيديو لإخفائها من عرض الصور لديك", "no_assets_message": "انقر لتحميل صورتك الأولى", "no_assets_to_show": "لا توجد أصول لعرضها", + "no_cast_devices_found": "لم يتم ايجاد جهاز بث", "no_duplicates_found": "لم يتم العثور على أي تكرارات.", "no_exif_info_available": "لا تتوفر معلومات exif", "no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.", "no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو", "no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك", + "no_locked_photos_message": "الصور والفديوهات في المجلد المقفل مخفية ولن تصهر في التصفح او البحث في مكتبتك.", "no_name": "لا اسم", + "no_notifications": "لا توجد تنبيهات", + "no_people_found": "لم يتم العثور على اشخاص مطابقين", "no_places": "لا أماكن", "no_results": "لا يوجد نتائج", "no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية", "no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك", "not_in_any_album": "ليست في أي ألبوم", - "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", + "not_selected": "لم يختار", + "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق سمة التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", "notes": "ملاحظات", + "nothing_here_yet": "لا يوجد شيء هنا بعد", "notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.", "notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.", "notification_permission_list_tile_enable_button": "تمكين الإخطارات", @@ -1120,16 +1322,22 @@ "notification_toggle_setting_description": "تفعيل إشعارات البريد الإلكتروني", "notifications": "إشعارات", "notifications_setting_description": "إدارة الإشعارات", + "oauth": "OAuth", "official_immich_resources": "الموارد الرسمية لشركة Immich", "offline": "غير متصل", "ok": "نعم", "oldest_first": "الأقدم أولا", + "on_this_device": "على هذا الجهاز", "onboarding": "الإعداد الأولي", - "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في إعدادات الإدارة.", + "onboarding_locale_description": "اختر لغتك المفضلة. يمكنك تغييرها فيما بعد في الاعدادات.", + "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في الاعدادات.", + "onboarding_server_welcome_description": "لنقم باعداد نسختك من البرنامج مع بعض الاعدادات الشائعة.", "onboarding_theme_description": "اختر نسق الألوان للنسخة الخاصة بك. يمكنك تغيير ذلك لاحقًا في إعداداتك.", + "onboarding_user_welcome_description": "لنساعدك على البدء!", "onboarding_welcome_user": "مرحبا، {user}", "online": "متصل", "only_favorites": "المفضلة فقط", + "open": "فتح", "open_in_map_view": "فتح في عرض الخريطة", "open_in_openstreetmap": "فتح في OpenStreetMap", "open_the_search_filters": "افتح مرشحات البحث", @@ -1146,12 +1354,14 @@ "partner_can_access": "يستطيع {partner} الوصول", "partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة", "partner_can_access_location": "الموقع الذي تم التقاط صورك فيه", + "partner_list_user_photos": "صور {user}", "partner_list_view_all": "عرض الكل", "partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.", "partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة", "partner_page_partner_add_failed": "فشل في إضافة شريك", "partner_page_select_partner": "حدد شريكًا", "partner_page_shared_to_title": "مشترك ل", + "partner_page_stop_sharing_content": "{partner} لن يعود قادرا على الوصوف الى صورك.", "partner_sharing": "مشاركة الشركاء", "partners": "الشركاء", "password": "كلمة المرور", @@ -1180,14 +1390,16 @@ "permanently_delete_assets_prompt": "هل أنت متأكد أنك تريد حذف {count, plural, one {هذا العنصر؟} other {هذه العناصر #؟}} سيتم أيضًا إزالته {count, plural, one {من ألبومه} other {من ألبوماتهم}}.", "permanently_deleted_asset": "تم حذف الأصل بشكل نهائي", "permanently_deleted_assets_count": "تم حذف {count, plural, one {# محتوى} other {# المحتويات}} نهائيًا", + "permission": "اذن", + "permission_empty": "الاذن الخاص بك يجب ان لا يكون فارغا", "permission_onboarding_back": "خلف", "permission_onboarding_continue_anyway": "تواصل على أي حال", "permission_onboarding_get_started": "البدء", "permission_onboarding_go_to_settings": "اذهب للاعدادات", - "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات ", + "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات.", "permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.", "permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.", - "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك", + "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك.", "person": "شخص", "person_birthdate": "تاريخ الميلاد {التاريخ}", "person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}", @@ -1197,9 +1409,10 @@ "photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}", "photos_from_previous_years": "صور من السنوات السابقة", "pick_a_location": "اختر موقعًا", - "pin_code_changed_successfully": "تم تغير الرقم السري", - "pin_code_reset_successfully": "تم اعادة تعيين الرقم السري", - "pin_code_setup_successfully": "تم انشاء رقم سري", + "pin_code_changed_successfully": "تم تغير رمز PIN بنجاح", + "pin_code_reset_successfully": "تم اعادة تعيين رمز PIN بنجاح", + "pin_code_setup_successfully": "تم انشاء رمز PIN بنجاح", + "pin_verification": "التحقق برمز PIN", "place": "مكان", "places": "الأماكن", "places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}", @@ -1207,15 +1420,21 @@ "play_memories": "تشغيل الذكريات", "play_motion_photo": "تشغيل الصور المتحركة", "play_or_pause_video": "تشغيل الفيديو أو إيقافه مؤقتًا", + "please_auth_to_access": "الرجاء القيام بالمصادقة للوصول", "port": "المنفذ", + "preferences_settings_subtitle": "ادارة تفضيلات التطبيق", "preferences_settings_title": "التفضيلات", "preset": "الإعداد المسبق", "preview": "معاينة", "previous": "السابق", "previous_memory": "الذكرى السابقة", - "previous_or_next_photo": "الصورة السابقة أو التالية", + "previous_or_next_day": "يوم تالي/سابق", + "previous_or_next_month": "شهر تالي/سابق", + "previous_or_next_photo": "صورة تالية/سابقة", + "previous_or_next_year": "سنة تالية/سابقة", "primary": "أساسي", "privacy": "الخصوصية", + "profile": "حساب تعريفي", "profile_drawer_app_logs": "السجلات", "profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.", "profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.", @@ -1247,7 +1466,7 @@ "purchase_lifetime_description": "الشراء لمدى الحياة", "purchase_option_title": "خيارات الشراء", "purchase_panel_info_1": "يتطلب بناء Immich الكثير من الوقت والجهد، ولدينا مهندسون يعملون بدوام كامل لجعله أفضل ما يمكن. مهمتنا هي أن تصبح البرمجيات مفتوحة المصدر وممارسات العمل الأخلاقية مصدر دخل مستدام للمطورين وإنشاء نظام بيئي يحترم الخصوصية مع بدائل حقيقية للخدمات السحابية الاستغلالية.", - "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة نظام حظر الاشتراك غير المدفوع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", + "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة حاجز دفع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", "purchase_panel_title": "ادعم المشروع", "purchase_per_server": "لكل خادم", "purchase_per_user": "لكل مستخدم", @@ -1272,13 +1491,16 @@ "recent": "حديث", "recent-albums": "ألبومات الحديثة", "recent_searches": "عمليات البحث الأخيرة", + "recently_added": "اضيف مؤخرا", "recently_added_page_title": "أضيف مؤخرا", + "recently_taken": "تم التقاطها مؤخرًا", + "recently_taken_page_title": "تم التقاطها مؤخرًا", "refresh": "تحديث", "refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة", "refresh_faces": "تحديث الوجوه", "refresh_metadata": "تحديث البيانات الوصفية", "refresh_thumbnails": "تحديث الصور المصغرة", - "refreshed": "تم التحديث", + "refreshed": "اعادة تحميل", "refreshes_every_file": "إعادة قراءة كافة الملفات الموجودة والجديدة", "refreshing_encoded_video": "جارٍ تحديث الفيديو المرمز", "refreshing_faces": "جاري تحديث الوجوه", @@ -1292,12 +1514,16 @@ "remove_deleted_assets": "إزالة الملفات الغير متصلة", "remove_from_album": "إزالة من الألبوم", "remove_from_favorites": "إزالة من المفضلة", + "remove_from_lock_folder_action_prompt": "{count} أويل من المجلد المقفل", + "remove_from_locked_folder": "ازالة من المجلد المقفل", + "remove_from_locked_folder_confirmation": "هل انت متأكد من ازالة هذه الصور والفيديوهات من المجلد المقفل؟ سيكونون مرئيين في المكتبة الخاصة بك.", "remove_from_shared_link": "إزالة من الرابط المشترك", "remove_memory": "إزالة الذاكرة", "remove_photo_from_memory": "إزالة الصورة من هذه الذكرى", + "remove_tag": "ازالة علامة", "remove_url": "إزالة عنوان URL", "remove_user": "إزالة المستخدم", - "removed_api_key": "تم إزالة مفتاح API: {name}", + "removed_api_key": "تم إزاة مفتاح API: ‪‫{name}", "removed_from_archive": "تمت إزالتها من الأرشيف", "removed_from_favorites": "تمت الإزالة من المفضلة", "removed_from_favorites_count": "{count, plural, other {أُزيلت #}} من التفضيلات", @@ -1315,6 +1541,7 @@ "reset": "إعادة ضبط", "reset_password": "إعادة تعيين كلمة المرور", "reset_people_visibility": "إعادة ضبط ظهور الأشخاص", + "reset_pin_code": "اعادة تعيين رمز PIN", "reset_to_default": "إعادة التعيين إلى الافتراضي", "resolve_duplicates": "معالجة النسخ المكررة", "resolved_all_duplicates": "تم حل جميع التكرارات", @@ -1329,6 +1556,7 @@ "role_editor": "المحرر", "role_viewer": "العارض", "save": "حفظ", + "save_to_gallery": "حفظ الى المعرض", "saved_api_key": "تم حفظ مفتاح الـ API", "saved_profile": "تم حفظ الملف", "saved_settings": "تم حفظ الإعدادات", @@ -1349,19 +1577,33 @@ "search_camera_model": "البحث حسب موديل الكاميرا...", "search_city": "البحث حسب المدينة...", "search_country": "البحث حسب الدولة...", - "search_filter_apply": "اختار الفلتر ", + "search_filter_apply": "اختار الفلتر", + "search_filter_camera_title": "اختر نوع الكاميرا", + "search_filter_date": "تاريخ", + "search_filter_date_interval": "{start} الى {end}", + "search_filter_date_title": "حدد نطاق التاريخ", "search_filter_display_option_not_in_album": "ليس في الألبوم", + "search_filter_display_options": "خيارات العرض", + "search_filter_filename": "بحث باستخدام الاسم", + "search_filter_location": "الموقع", + "search_filter_location_title": "اختر الموقع", + "search_filter_media_type": "نوع الوسائط", + "search_filter_media_type_title": "اختر نوع الوسائط", + "search_filter_people_title": "اختر الاشخاص", "search_for": "البحث عن", "search_for_existing_person": "البحث عن شخص موجود", + "search_no_more_result": "لا توجد نتائج اضافية", "search_no_people": "لا يوجد أشخاص", "search_no_people_named": "لا يوجد أشخاص بالاسم \"{name}\"", + "search_no_result": "لا توجد نتائج، حاول استخدام مصطلح بحث مختلف او تركيبة", "search_options": "خيارات البحث", "search_page_categories": "فئات", "search_page_motion_photos": "الصور المتحركه", "search_page_no_objects": "لا توجد معلومات عن أشياء متاحة", "search_page_no_places": "لا توجد معلومات متوفرة للأماكن", "search_page_screenshots": "لقطات الشاشة", - "search_page_selfies": " صور ذاتيه", + "search_page_search_photos_videos": "ابحث عن صورك او فديوهاتك", + "search_page_selfies": "صور ذاتيه", "search_page_things": "أشياء", "search_page_view_all_button": "عرض الكل", "search_page_your_activity": "نشاطك", @@ -1372,7 +1614,7 @@ "search_result_page_new_search_hint": "بحث جديد", "search_settings": "إعدادات البحث", "search_state": "البحث حسب الولاية...", - "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة", + "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة. ", "search_suggestion_list_smart_search_hint_2": "م: البحث الخاص بك", "search_tags": "البحث عن العلامات...", "search_timezone": "البحث حسب المنطقة الزمنية...", @@ -1385,6 +1627,7 @@ "select_album_cover": "تحديد غلاف الألبوم", "select_all": "تحديد الكل", "select_all_duplicates": "تحديد جميع النسخ المكررة", + "select_all_in": "اختر الكل في {group}", "select_avatar_color": "تحديد لون الصورة الشخصية", "select_face": "تحديد وجه", "select_featured_photo": "تحديد الصورة المميزة", @@ -1392,6 +1635,7 @@ "select_keep_all": "تحديد الأحتفاظ بالكل", "select_library_owner": "تحديد مالِك المكتبة", "select_new_face": "تحديد وجه جديد", + "select_person_to_tag": "اختر شخص لوضع علامة", "select_photos": "تحديد الصور", "select_trash_all": "تحديد حذف الكلِ", "select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم", @@ -1399,10 +1643,12 @@ "selected_count": "{count, plural, other {# محددة }}", "send_message": "‏إرسال رسالة", "send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا", + "server_endpoint": "نقطة نهاية الخادم", "server_info_box_app_version": "نسخة التطبيق", "server_info_box_server_url": "عنوان URL الخادم", "server_offline": "الخادم غير متصل", "server_online": "الخادم متصل", + "server_privacy": "خصوصية الخادم", "server_stats": "إحصائيات الخادم", "server_version": "إصدار الخادم", "set": "‏تحديد", @@ -1412,6 +1658,7 @@ "set_date_of_birth": "تحديد تاريخ الميلاد", "set_profile_picture": "تحديد صورة الملف الشخصي", "set_slideshow_to_fullscreen": "تحديد عرض الشرائح على وضع ملء الشاشة", + "set_stack_primary_asset": "تعيين كأصل اساسي", "setting_image_viewer_help": "يقوم عارض التفاصيل بتحميل الصورة المصغرة الصغيرة أولاً ، ثم يقوم بتحميل المعاينة متوسطة الحجم (إذا تم تمكينها) ، ويقوم أخيرًا بتحميل الأصل (إذا تم تمكينه).", "setting_image_viewer_original_subtitle": "تمكين تحميل الصورة الكاملة الدقة الأصلية (كبيرة!).تعطيل لتقليل استخدام البيانات (كل من الشبكة وعلى ذاكرة التخزين المؤقت للجهاز).", "setting_image_viewer_original_title": "تحميل الصورة الأصلية", @@ -1419,21 +1666,30 @@ "setting_image_viewer_preview_title": "تحميل صورة معاينة", "setting_image_viewer_title": "الصور", "setting_languages_apply": "تغيير الإعدادات", + "setting_languages_subtitle": "تغيير لغة التطبيق", + "setting_notifications_notify_failures_grace_period": "التنبيه بفشل النسخ الاحتياطي في الخلفية: {duration}", + "setting_notifications_notify_hours": "{count} ساعات", "setting_notifications_notify_immediately": "في الحال", + "setting_notifications_notify_minutes": "{count} دقائق", "setting_notifications_notify_never": "أبداً", + "setting_notifications_notify_seconds": "{count} ثواني", "setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل", "setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية", "setting_notifications_subtitle": "اضبط تفضيلات الإخطار", "setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)", "setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز", "setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا", + "setting_video_viewer_original_video_subtitle": "عند بث فيديو من الخادم، شغّل النسخة الأصلية حتى مع توفر ترميز بديل. قد يؤدي ذلك إلى تقطيع اثناء العرض . تُشغّل الفيديوهات المتوفرة محليًا بجودة أصلية بغض النظر عن هذا الإعداد.", + "setting_video_viewer_original_video_title": "اجبار عرض الفديو الاصلي", "settings": "الإعدادات", "settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد", "settings_saved": "تم حفظ الإعدادات", - "setup_pin_code": "تحديد رقم سري", + "setup_pin_code": "تحديد رمز PIN", "share": "مشاركة", "share_add_photos": "إضافة الصور", + "share_assets_selected": "اختيار {count}", "share_dialog_preparing": "تحضير...", + "share_link": "مشاركة رابط", "shared": "مُشتَرك", "shared_album_activities_input_disable": "التعليق معطل", "shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟", @@ -1446,22 +1702,40 @@ "shared_by_user": "تمت المشاركة بواسطة {user}", "shared_by_you": "تمت مشاركته من قِبلك", "shared_from_partner": "صور من {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} تم رفع", "shared_link_app_bar_title": "روابط مشتركة", "shared_link_clipboard_copied_massage": "نسخ إلى الحافظة", + "shared_link_clipboard_text": "رابط: {link}\nكلمة المرور: {password}", "shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك", "shared_link_edit_description_hint": "أدخل وصف المشاركة", "shared_link_edit_expire_after_option_day": "يوم 1", + "shared_link_edit_expire_after_option_days": "{count} ايام", "shared_link_edit_expire_after_option_hour": "1 ساعة", + "shared_link_edit_expire_after_option_hours": "{count} ساعات", "shared_link_edit_expire_after_option_minute": "1 دقيقة", + "shared_link_edit_expire_after_option_minutes": "{count} دقائق", + "shared_link_edit_expire_after_option_months": "{count} اشهر", + "shared_link_edit_expire_after_option_year": "{count} سنة", "shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة", "shared_link_edit_submit_button": "تحديث الرابط", "shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم", + "shared_link_expires_day": "تنتهي صلاحيته في {count} يوم", + "shared_link_expires_days": "تنتهي صلاحيته في {count} ايام", + "shared_link_expires_hour": "تنتهي صلاحية في {count} ساعة", + "shared_link_expires_hours": "تنتهي صلاحيته في {count} ساعات", + "shared_link_expires_minute": "تنتهي صلاحيته في {count} دقيقة", + "shared_link_expires_minutes": "تنتهي صلاحيته في {count} دقائق", "shared_link_expires_never": "تنتهي ∞", + "shared_link_expires_second": "تنتهي صلاحيته في {count} ثانية", + "shared_link_expires_seconds": "تنتهي صلاحيته في {count} ثواني", + "shared_link_individual_shared": "مشاركة فردية", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "إدارة الروابط المشتركة", "shared_link_options": "خيارات الرابط المشترك", "shared_links": "روابط مشتركة", "shared_links_description": "وصف الروابط المشتركة", "shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}", + "shared_with_me": "تمت مشاركتها معي", "shared_with_partner": "تمت المشاركة مع {partner}", "sharing": "مشاركة", "sharing_enter_password": "الرجاء إدخال كلمة المرور لعرض هذه الصفحة.", @@ -1522,12 +1796,14 @@ "start_date": "تاريخ البدء", "state": "الولاية", "status": "الحالة", + "stop_casting": "ايقاف البث", "stop_motion_photo": "إيقاف حركة الصورة", "stop_photo_sharing": "توقف عن مشاركة صورك؟", "stop_photo_sharing_description": "لن يتمكن {partner} من الوصول إلى صورك بعد الآن.", "stop_sharing_photos_with_user": "توقف عن مشاركة صورك مع هذا المستخدم", "storage": "مساحة التخزين", - "storage_label": "تسمية التخزين", + "storage_label": "سمة التخزين", + "storage_quota": "حصة الخزن", "storage_usage": "{used} من {available} مُستخْدم", "submit": "إرسال", "suggestions": "اقتراحات", @@ -1537,6 +1813,9 @@ "support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.", "swap_merge_direction": "تبديل اتجاه الدمج", "sync": "مزامنة", + "sync_albums": "مزامنة الالبومات", + "sync_albums_manual_subtitle": "مزامنة جميع الفديوهات والصور المرفوعة الى البومات الخزن الاحتياطي المختارة", + "sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich", "tag": "العلامة", "tag_assets": "أصول العلامة", "tag_created": "تم إنشاء العلامة: {tag}", @@ -1551,8 +1830,14 @@ "theme_selection": "اختيار السمة", "theme_selection_description": "قم بتعيين السمة تلقائيًا على اللون الفاتح أو الداكن بناءً على تفضيلات نظام المتصفح الخاص بك", "theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول", + "theme_setting_asset_list_tiles_per_row_title": "عدد الاصول في كل صف({count})", + "theme_setting_colorful_interface_subtitle": "تطبيق اللون الأساسي على الأسطح في الخلفية.", + "theme_setting_colorful_interface_title": "واجهه ملونة", "theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية", "theme_setting_image_viewer_quality_title": "جودة عارض الصورة", + "theme_setting_primary_color_subtitle": "اختر لون للعمليات الاساسية والمكمله.", + "theme_setting_primary_color_title": "اللون الاساسي", + "theme_setting_system_primary_color_title": "استخدم لون النظام", "theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)", "theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق", "theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير", @@ -1572,22 +1857,29 @@ "total": "الإجمالي", "total_usage": "الاستخدام الإجمالي", "trash": "المهملات", + "trash_action_prompt": "{count} نقل الى سلة المهملات", "trash_all": "نقل الكل إلى سلة المهملات", "trash_count": "سلة المحملات {count, number}", "trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات", + "trash_emptied": "سبة مهملا مفرغة", "trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.", "trash_page_delete_all": "حذف الكل", "trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق", + "trash_page_info": "العناصر المنقولة الى سلة المهملات سيتم حذفها بشكل نهائي بعد {days} ايام", "trash_page_no_assets": "لا توجد اصول في سله المهملات", "trash_page_restore_all": "استعادة الكل", - "trash_page_select_assets_btn": "اختر الأصول ", + "trash_page_select_assets_btn": "اختر الأصول", + "trash_page_title": "سلة المهملات ({count})", "trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.", "type": "النوع", - "unable_to_change_pin_code": "تفيير الرقم السري غير ممكن", - "unable_to_setup_pin_code": "انشاء الرقم السري غير ممكن", + "unable_to_change_pin_code": "تفيير رمز PIN غير ممكن", + "unable_to_setup_pin_code": "انشاء رمز PIN غير ممكن", "unarchive": "أخرج من الأرشيف", + "unarchive_action_prompt": "{count} ازيل من الارشيف", "unarchived_count": "{count, plural, other {غير مؤرشفة #}}", + "undo": "تراجع", "unfavorite": "أزل التفضيل", + "unfavorite_action_prompt": "{count} ازيل من المفضلات", "unhide_person": "أظهر الشخص", "unknown": "غير معروف", "unknown_country": "بلد غير معروف", @@ -1603,9 +1895,11 @@ "unsaved_change": "تغيير غير محفوظ", "unselect_all": "إلغاء تحديد الكل", "unselect_all_duplicates": "إلغاء تحديد كافة النسخ المكررة", + "unselect_all_in": "إلغاء تحديد الكل في {group}", "unstack": "فك الكومه", "unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس", "up_next": "التالي", + "updated_at": "تم التحديث", "updated_password": "تم تحديث كلمة المرور", "upload": "رفع", "upload_concurrency": "الرفع المتزامن", @@ -1618,14 +1912,20 @@ "upload_status_errors": "الأخطاء", "upload_status_uploaded": "تم الرفع", "upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.", + "upload_to_immich": "الرفع الىImmich ‎ ‏ ({count})", + "uploading": "جاري الرفع", "url": "عنوان URL", "usage": "الاستخدام", + "use_biometric": "استخدم البايومتري", + "use_current_connection": "استخدم الاتصال الحالي", "use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك", "user": "مستخدم", + "user_has_been_deleted": "هذا المستخدم تم حذفه.", "user_id": "معرف المستخدم", "user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}", - "user_pin_code_settings": "الرقم السري", - "user_pin_code_settings_description": "تغير الرقم السري", + "user_pin_code_settings": "رمز PIN", + "user_pin_code_settings_description": "تغير رمز PIN", + "user_privacy": "خصوصية المستخدم", "user_purchase_settings": "الشراء", "user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك", "user_role_set": "قم بتعيين {user} كـ {role}", @@ -1636,6 +1936,7 @@ "users": "المستخدمين", "utilities": "أدوات", "validate": "تحقْق", + "validate_endpoint_error": "الرجاء ادخال عنوان URL صالح", "variables": "المتغيرات", "version": "الإصدار", "version_announcement_closing": "صديقك، أليكس", @@ -1657,7 +1958,9 @@ "view_name": "عرض", "view_next_asset": "عرض المحتوى التالي", "view_previous_asset": "عرض المحتوى السابق", + "view_qr_code": "­عرض رمز الاستجابة السريعة", "view_stack": "عرض التكديس", + "view_user": "عرض المستخدم", "viewer_remove_from_stack": "حذف من الكومه أو المجموعة", "viewer_stack_use_as_main_asset": "استخدم كأصل رئيسي", "viewer_unstack": "فك الكومه", @@ -1667,11 +1970,12 @@ "week": "أسبوع", "welcome": "مرحباً", "welcome_to_immich": "مرحباً بك في Immich", - "wifi_name": "WiFi Name", + "wifi_name": "اسم شبكة Wi-Fi", + "wrong_pin_code": "رمز PIN خاطئ", "year": "سنة", "years_ago": "منذ {years, plural, one {# سنة} other {# سنوات}}", "yes": "نعم", "you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "اسم شبكة Wi-Fi الخاص بك", "zoom_image": "تكبير الصورة" } diff --git a/i18n/be.json b/i18n/be.json index 470030b6f6..d3f59b32a5 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -22,6 +22,7 @@ "add_partner": "Дадаць партнёра", "add_path": "Дадаць шлях", "add_photos": "Дадаць фота", + "add_tag": "Дадаць тэг", "add_to": "Дадаць у…", "add_to_album": "Дадаць у альбом", "add_to_album_bottom_sheet_added": "Дададзена да {album}", @@ -33,28 +34,30 @@ "added_to_favorites_count": "Дададзена {count, number} да абранага", "admin": { "add_exclusion_pattern_description": "Дадайце шаблоны выключэнняў. Падтрымліваецца выкарыстанне сімвалаў * , ** і ?. Каб ігнараваць усе файлы ў любой дырэкторыі з назвай \"Raw\", выкарыстоўвайце \"**/Raw/**\". Каб ігнараваць усе файлы, якія заканчваюцца на \".tif\", выкарыстоўвайце \"**/.tif\". Каб ігнараваць абсолютны шлях, выкарыстоўвайце \"/path/to/ignore/**\".", + "admin_user": "Адміністратар", "asset_offline_description": "Гэты знешні бібліятэчны актыў больш не знойдзены на дыску і быў перамешчаны ў сметніцу. Калі файл быў перамешчаны ў межах бібліятэкі, праверце вашу хроніку для новага адпаведнага актыва. Каб аднавіць гэты актыў, пераканайцеся, што шлях да файла ніжэй даступны для Immich і адскануйце бібліятэку.", "authentication_settings": "Налады праверкі сапраўднасці", "authentication_settings_description": "Кіраванне паролямі, OAuth, і іншыя налады праверкі сапраўднасці", "authentication_settings_disable_all": "Вы ўпэўнены, што жадаеце адключыць усе спосабы логіну? Логін будзе цалкам адключаны.", "authentication_settings_reenable": "Каб зноў уключыць, выкарыстайце Каманду сервера.", "background_task_job": "Фонавыя заданні", - "backup_database": "Рэзервовая копія базы даных", + "backup_database": "Стварыць рэзервовую копію базы даных", "backup_database_enable_description": "Уключыць рэзерваванне базы даных", "backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання", "backup_settings": "Налады рэзервовага капіявання", - "backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.", + "backup_settings_description": "Кіраванне наладамі рэзервавання базы даных.", "cleared_jobs": "Ачышчаны заданні для: {job}", - "config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі", - "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?", + "config_set_by_file": "Канфігурацыя зараз усталявана праз файл канфігурацыі", + "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць бібліятэку {library}?", "confirm_delete_library_assets": "Вы ўпэўнены, што хочаце выдаліць гэтую бібліятэку? Гэта прывядзе да выдалення {count, plural, one {# актыву} other {усіх # актываў}}, якія змяшчаюцца ў Immich, і гэта дзеянне немагчыма будзе адмяніць. Файлы застануцца на дыску.", "confirm_email_below": "Каб пацвердзіць, увядзіце \"{email}\" ніжэй", "confirm_reprocess_all_faces": "Вы ўпэўнены, што хочаце пераапрацаваць усе твары? Гэта таксама прывядзе да выдалення імя людзей.", "confirm_user_password_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць пароль {user}?", + "confirm_user_pin_code_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць PIN-код {user}?", "create_job": "Стварыць заданне", "cron_expression": "Выраз Cron", "cron_expression_description": "Усталюйце інтэрвал сканавання, выкарыстоўваючы фармат cron. Для атрымання дадатковай інфармацыі, калі ласка, звярніцеся, напрыклад, да Crontab Guru", - "cron_expression_presets": "Прадустановкі выразаў Cron", + "cron_expression_presets": "Прадустаноўкі выразаў Cron", "disable_login": "Адключыць уваход", "duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search", "exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.", @@ -71,15 +74,272 @@ "image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.", "image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.", "image_fullsize_title": "Налады выявы ў поўным памеры", + "image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.", + "image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме", "image_preview_title": "Налады папярэдняга прагляду", "image_quality": "Якасць", "image_resolution": "Раздзяляльнасць", "image_settings": "Налады відарыса", - "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў" + "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў", + "library_created": "Створана бібліятэка: {library}", + "library_deleted": "Бібліятэка выдалена", + "map_dark_style": "Цёмны стыль", + "map_enable_description": "Уключыць функцыі карты", + "map_gps_settings": "Налады карты і GPS", + "map_light_style": "Светлы стыль", + "map_settings": "Карта", + "map_settings_description": "Кіраванне наладамі карты", + "map_style_description": "URL-адрас style.json тэмы карты", + "metadata_settings": "Налады метаданых", + "oauth_button_text": "Тэкст кнопкі", + "oauth_settings": "OAuth", + "system_settings": "Сістэмныя налады", + "theme_settings": "Налады тэмы", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_audio_codec": "Аудыякодэк", + "transcoding_video_codec": "Відэакодэк", + "trash_settings": "Налады сметніцы", + "trash_settings_description": "Кіраванне наладамі сметніцы", + "version_check_settings": "Праверка версіі", + "version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі" }, + "advanced_settings_troubleshooting_title": "Выпраўленне непаладак", + "album_added": "Альбом дададзены", + "album_name": "Назва альбома", + "album_remove_user": "Выдаліць карыстальніка?", + "album_updated": "Альбом абноўлены", + "albums": "Альбомы", + "all": "Усе", + "all_albums": "Усе альбомы", + "all_people": "Усе людзі", + "all_videos": "Усе відэа", + "app_bar_signout_dialog_ok": "Так", + "app_bar_signout_dialog_title": "Выйсці", + "app_settings": "Налады праграмы", + "archive": "Архіў", + "archive_size": "Памер архіва", + "asset_uploading": "Запампоўванне…", + "back": "Назад", + "backup_all": "Усе", + "backup_controller_page_background_wifi": "Толькі праз Wi-Fi", + "buy": "Купіць Immich", + "cache_settings_clear_cache_button": "Ачысціць кэш", + "cache_settings_tile_title": "Лакальнае сховішча", + "cancel": "Скасаваць", + "cancel_search": "Скасаваць пошук", + "canceled": "Скасавана", + "city": "Горад", + "clear": "Ачысціць", + "clear_all": "Ачысціць усё", + "client_cert_dialog_msg_confirm": "ОК", + "client_cert_enter_password": "Увядзіце пароль", + "client_cert_import": "Імпарт", + "close": "Закрыць", + "collapse": "Згарнуць", + "collapse_all": "Згарнуць усё", + "color": "Колер", + "color_theme": "Колеравая тэма", + "continue": "Працягнуць", + "control_bottom_app_bar_create_new_album": "Стварыць новы альбом", + "control_bottom_app_bar_delete_from_immich": "Выдаліць з Immich", + "control_bottom_app_bar_delete_from_local": "Выдаліць з прылады", + "control_bottom_app_bar_edit_location": "Рэдагаваць месцазнаходжанне", + "country": "Краіна", + "cover": "Вокладка", + "covers": "Вокладкі", + "create": "Стварыць", + "create_album": "Стварыць альбом", + "create_album_page_untitled": "Без назвы", + "create_library": "Стварыць бібліятэку", + "create_link": "Стварыць спасылку", + "create_new_user": "Стварыць новага карыстальніка", + "create_tag": "Стварыць тэг", + "create_user": "Стварыць карыстальніка", + "dark": "Цёмная", + "day": "Дзень", + "delete": "Выдаліць", + "delete_album": "Выдаліць альбом", + "delete_dialog_ok_force": "Усё адно выдаліць", + "delete_dialog_title": "Выдаліць назаўжды", + "delete_face": "Выдаліць твар", + "delete_key": "Выдаліць ключ", + "delete_library": "Выдаліць бібліятэку", + "delete_link": "Выдаліць спасылку", + "delete_local_dialog_ok_force": "Усё адно выдаліць", + "delete_others": "Выдаліць іншыя", + "delete_tag": "Выдаліць тэг", + "delete_user": "Выдаліць карыстальніка", + "discord": "Discord", + "documentation": "Дакументацыя", + "done": "Гатова", + "download": "Спампаваць", + "download_canceled": "Спампоўванне скасавана", + "download_complete": "Спампоўванне завершана", + "download_enqueue": "Спампоўванне дададзена ў чаргу", + "downloading": "Спампоўванне", + "edit": "Рэдагаваць", + "edit_album": "Рэдагаваць альбом", + "edit_avatar": "Рэдагаваць аватар", + "edit_date": "Рэдагаваць дату", + "edit_date_and_time": "Рэдагаваь дату і час", + "edit_description": "Рэдагаваць апісанне", + "edit_description_prompt": "Выберыце новае апісанне:", + "edit_faces": "Рэдагаваць твары", + "edit_import_path": "Рэдагаваць шлях імпарту", + "edit_import_paths": "Рэдагаваць шляхі імпарту", + "edit_key": "Рэдагаваць ключ", + "edit_link": "Рэдагаваць спасылку", + "edit_location": "Рэдагаваць месцазнаходжанне", + "edit_location_dialog_title": "Месцазнаходжанне", + "edit_name": "Рэдагаваць назву", + "edit_people": "Рэдагаваць людзей", + "edit_tag": "Рэдагаваць тэг", + "edit_title": "Рэдагаваць загаловак", + "edit_user": "Рэдагаваць карыстальніка", + "edited": "Адрэдагавана", + "editor": "Рэдактар", + "editor_close_without_save_prompt": "Змены не будуць захаваны", + "editor_close_without_save_title": "Закрыць рэдактар?", + "editor_crop_tool_h2_aspect_ratios": "Суадносіны бакоў", + "editor_crop_tool_h2_rotation": "Паварот", + "error": "Памылка", + "error_saving_image": "Памылка: {error}", + "exif": "Exif", + "exif_bottom_sheet_description": "Дадаць апісанне...", + "favorite": "У абраным", + "favorite_or_unfavorite_photo": "Дадаць або выдаліць фота з абранага", + "favorites": "Абраныя", + "file_name": "Назва файла", + "filename": "Назва файла", + "filetype": "Тып файла", + "filter": "Фільтр", + "forward": "Наперад", + "gcast_enabled": "Google Cast", + "general": "Агульныя", + "go_back": "Назад", + "go_to_folder": "Перайсці да папкі", + "hi_user": "Вітаем, {name} ({email})", + "hide_all_people": "Схаваць усіх людзей", + "hide_gallery": "Схаваць галерэю", + "hide_named_person": "Схаваць {name}", + "hide_password": "Схаваць пароль", + "hide_person": "Схаваць чалавека", + "image_viewer_page_state_provider_download_started": "Спампоўванне пачалося", + "immich_logo": "Лагатып Immich", + "interval": { + "day_at_onepm": "Кожны дзень а 13-й гадзіне", + "hours": "{hours, plural, one {Кожную гадзіну} few {Кожныя {hours, number} гадзіны} many {Кожныя {hours, number} гадзін} other {Кожныя {hours, number} гадзін}}", + "night_at_midnight": "Кожную ноч апоўначы", + "night_at_twoam": "Кожную ноч а 2-й гадзіне" + }, + "language": "Мова", + "library": "Бібліятэка", + "light": "Светлая", + "login_form_back_button_text": "Назад", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_password_hint": "пароль", + "login_form_save_login": "Заставацца ў сістэме", + "main_menu": "Галоўнае меню", + "map_location_dialog_yes": "Так", + "map_settings_dark_mode": "Цёмны рэжым", + "map_settings_date_range_option_day": "Апошнія 24 гадзіны", + "map_settings_date_range_option_days": "Апошніх дзён: {days}", + "map_settings_date_range_option_year": "Апошні год", + "map_settings_date_range_option_years": "Апошніх год: {years}", + "map_settings_dialog_title": "Налады карты", + "map_settings_theme_settings": "Тэма карты", + "menu": "Меню", + "minute": "Хвіліна", + "month": "Месяц", + "monthly_title_text_date_format": "MMMM y", + "my_albums": "Мае альбомы", + "name": "Імя", + "name_or_nickname": "Імя або псеўданім", + "next": "Далей", + "no": "Не", + "offline": "Па-за сеткай", + "ok": "ОК", + "online": "У сетцы", + "open": "Адкрыць", + "or": "або", + "partner_list_user_photos": "Фота карыстальніка {user}", + "pause": "Прыпыніць", + "people": "Людзі", + "permission_onboarding_back": "Назад", + "permission_onboarding_continue_anyway": "Усё адно працягнуць", + "photos": "Фота", + "photos_and_videos": "Фота і відэа", + "place": "Месца", + "places": "Месцы", + "port": "Порт", + "previous": "Папярэдняе", + "profile": "Профіль", + "profile_drawer_app_logs": "Журналы", + "profile_drawer_github": "GitHub", + "purchase_button_buy": "Купіць", + "purchase_button_buy_immich": "Купіць Immich", + "purchase_button_select": "Выбраць", + "remove": "Выдаліць", + "remove_from_album": "Выдаліць з альбома", + "remove_from_favorites": "Выдаліць з абраных", + "remove_tag": "Выдаліць тэг", + "remove_url": "Выдаліць URL-адрас", + "remove_user": "Выдаліць карыстальніка", + "rename": "Перайменаваць", + "repository": "Рэпазіторый", + "reset": "Скінуць", + "reset_password": "Скінуць пароль", + "restore": "Аднавіць", + "restore_all": "Аднавіць усё", + "restore_user": "Аднавіць карыстальніка", + "resume": "Узнавіць", + "role": "Роля", + "role_editor": "Рэдактар", + "role_viewer": "Глядач", + "save": "Захаваць", + "save_to_gallery": "Захаваць у галерэю", + "search_filter_date": "Дата", + "search_filter_location": "Месцазнаходжанне", + "search_filter_location_title": "Выберыце месцазнаходжанне", + "search_filter_media_type": "Тып медыя", + "search_filter_media_type_title": "Выберыце тып медыя", + "search_page_screenshots": "Здымкі экрана", + "search_page_selfies": "Сэлфі", + "search_page_things": "Рэчы", + "search_page_your_map": "Ваша карта", + "second": "Секунда", + "send_message": "Адправіць паведамленне", + "setting_languages_apply": "Ужыць", + "setting_notifications_notify_never": "ніколі", + "settings": "Налады", + "share_add_photos": "Дадаць фота", + "shared_album_section_people_title": "ЛЮДЗІ", + "shared_link_info_chip_metadata": "EXIF", + "sharing_page_empty_list": "ПУСТЫ СПІС", + "sign_out": "Выйсці", + "sign_up": "Зарэгістравацца", + "size": "Памер", + "sort_title": "Загаловак", + "source": "Крыніца", + "tag": "Тэг", + "tags": "Тэгі", + "theme": "Тэма", + "theme_selection": "Выбар тэмы", "timeline": "Хроніка", "total": "Усяго", + "trash": "Сметніца", + "trash_page_delete_all": "Выдаліць усе", + "trash_page_restore_all": "Аднавіць усе", + "trash_page_title": "Сметніца ({count})", + "type": "Тып", + "undo": "Адрабіць", + "upload": "Запампаваць", + "upload_status_errors": "Памылкі", + "uploading": "Запампоўванне", + "url": "URL-адрас", "user": "Карыстальнік", + "user_has_been_deleted": "Гэты карыстальнік быў выдалены.", "user_id": "ID карыстальніка", "user_purchase_settings": "Купля", "user_purchase_settings_description": "Кіруйце пакупкамі", @@ -112,14 +372,14 @@ "view_next_asset": "Паказаць наступны аб'ект", "view_previous_asset": "Праглядзець папярэдні аб'ект", "view_stack": "Прагляд стэка", - "visibility_changed": "Відзімасць змянілася для {count, plural, one {# чалавек(-аў)} астатніх {# чалавек}}", + "visibility_changed": "Бачнасць змянілася для {count, plural, one {# чалавека} other {# чалавек}}", "waiting": "Чакаюць", "warning": "Папярэджанне", "week": "Тыдзень", "welcome": "Вітаем", "welcome_to_immich": "Вітаем у Immich", "year": "Год", - "years_ago": "{years, plural, one {# год} other {# гадоў}} таму", + "years_ago": "{years, plural, one {# год} few {# гады} many {# гадоў} other {# гадоў}} таму", "yes": "Так", "you_dont_have_any_shared_links": "У вас няма абагуленых спасылак", "zoom_image": "Павялічыць відарыс" diff --git a/i18n/bg.json b/i18n/bg.json index 2df3dd927d..327fd7c7df 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Управление на настройките за метаданни", "migration_job": "Миграция", "migration_job_description": "Мигриране на миниатюрите за елементи и лица към най-новата структура на папките", + "nightly_tasks_cluster_faces_setting_description": "Изпълни разпознаване на лице за открити нови лица", + "nightly_tasks_cluster_new_faces_setting": "Разпознаване на нови лица", + "nightly_tasks_database_cleanup_setting": "Задачи по почистване на базата данни", + "nightly_tasks_database_cleanup_setting_description": "Премахни стари, ненужни записи от базата данни", + "nightly_tasks_generate_memories_setting": "Създаване на спомени", + "nightly_tasks_generate_memories_setting_description": "Създаване на нови спомени от съществуващи обекти", + "nightly_tasks_missing_thumbnails_setting": "Генериране на липсващи миниатюри", + "nightly_tasks_missing_thumbnails_setting_description": "Добавяне на обекти без миниатюра в опашката за създаване на миниатюра", + "nightly_tasks_settings": "Настройка на задачи за през нощта", + "nightly_tasks_settings_description": "Управление на задачите, изпълнявани през нощта", + "nightly_tasks_start_time_setting": "Време за начало", + "nightly_tasks_start_time_setting_description": "Време, когато сървъра ще започне изпълнение на нощни задачи", + "nightly_tasks_sync_quota_usage_setting": "Квота за синхронизация", + "nightly_tasks_sync_quota_usage_setting_description": "Обновяване на квотата според текущото потребление", "no_paths_added": "Няма добавени пътища", "no_pattern_added": "Няма добавен модел", "note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI за мобилно пренасочване", "oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства", "oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''", + "oauth_role_claim": "Потвърждение на роля", + "oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Управление на настройките за вход с OAuth", "oauth_settings_more_details": "За повече информация за функционалността, се потърсете в docs.", @@ -357,6 +373,8 @@ "admin_password": "Администраторска парола", "administration": "Администрация", "advanced": "Разширено", + "advanced_settings_beta_timeline_subtitle": "Опитайте новите функции на приложението", + "advanced_settings_beta_timeline_title": "Бета версия на времевата линия", "advanced_settings_enable_alternate_media_filter_subtitle": "При синхронизация, използвайте тази опция като филтър, основан на промяна на даден критерии. Опитайте само в случай, че приложението има проблем с откриване на всички албуми.", "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНТАЛНО] Използвай филтъра на алтернативното устройство за синхронизация на албуми", "advanced_settings_log_level_title": "Ниво на запис в дневника: {level}", @@ -427,6 +445,7 @@ "app_settings": "Настройки ма приложението", "appears_in": "Излиза в", "archive": "Архив", + "archive_action_prompt": "{count} са добавени в Архива", "archive_or_unarchive_photo": "Архивиране или деархивиране на снимка", "archive_page_no_archived_assets": "Не са намерени обекти в архива", "archive_page_title": "Архив ({count})", @@ -464,7 +483,6 @@ "assets": "Елементи", "assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума", - "assets_added_to_name_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} към {hasName, select, true {{name}} other {нов албум}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Обекта не може да се добави} other {Обектите не може да се добавят}} в албума", "assets_count": "{count, plural, one {# актив} other {# актива}}", "assets_deleted_permanently": "{count} обекта са изтрити завинаги", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "Тъмен", - "darkTheme": "Превключи на тъмна тема", + "dark_theme": "Тъмна тема", "date_after": "Дата след", "date_and_time": "Дата и час", "date_before": "Дата преди", @@ -719,6 +737,7 @@ "default_locale": "Локализация по подразбиране", "default_locale_description": "Форматиране на дати и числа в зависимост от езиковата настройка на браузъра", "delete": "Изтрий", + "delete_action_prompt": "{count} са изтрити завинаги", "delete_album": "Изтрий албум", "delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?", "delete_dialog_alert": "Тези обекти ще бъдат изтрити завинаги и от Immich сървъра и от устройството", @@ -732,19 +751,20 @@ "delete_key": "Изтрий ключ", "delete_library": "Изтрий библиотека", "delete_link": "Изтрий линк", + "delete_local_action_prompt": "{count} са изтрити локално", "delete_local_dialog_ok_backed_up_only": "Изтрий локално само архивираните", "delete_local_dialog_ok_force": "Въпреки това изтрий", "delete_others": "Изтрий останалите", "delete_shared_link": "Изтриване на споделен линк", "delete_shared_link_dialog_title": "Изтрий споделената връзка", "delete_tag": "Изтрий таг", - "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете таг {tagName}?", + "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете тага {tagName}?", "delete_user": "Изтрий потребител", "deleted_shared_link": "Изтрит споделен линк", "deletes_missing_assets": "Изтрива файлове, които липсват на диска", "description": "Описание", "description_input_hint_text": "Добави описание...", - "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности виж в дневника", + "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности вижте в дневника", "details": "Детайли", "direction": "Посока", "disabled": "Изключено", @@ -762,6 +782,7 @@ "documentation": "Документация", "done": "Готово", "download": "Изтегли", + "download_action_prompt": "Зареждане на {count} обекта", "download_canceled": "Изтеглянето е отменено", "download_complete": "Изтеглянето завърши", "download_enqueue": "Изтеглянето е добавено в опашката", @@ -799,6 +820,7 @@ "edit_key": "Редактиране на ключ", "edit_link": "Редактиране на линк", "edit_location": "Редактиране на местоположението", + "edit_location_action_prompt": "{count} локации са редактирани", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактиране на име", "edit_people": "Редактиране на хора", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Неуспешно зареждане на елементи", "failed_to_load_folder": "Неуспешно зареждане на папка", "favorite": "Любим", + "favorite_action_prompt": "{count} са добавени в Любими", "favorite_or_unfavorite_photo": "Добави или премахни снимка от Любими", "favorites": "Любими", "favorites_page_no_favorites": "Не са намерени любими обекти", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Дата на създаване", "library_page_sort_last_modified": "Последна промяна", "library_page_sort_title": "Заглавие на албума", + "licenses": "Лицензи", "light": "Светло", "like_deleted": "Като изтрит", "link_motion_video": "Линк към видео", @@ -1246,6 +1270,7 @@ "more": "Още", "move": "Премести", "move_off_locked_folder": "Извади от заключената папка", + "move_to_lock_folder_action_prompt": "{count} са добавени в заключената папка", "move_to_locked_folder": "Премести в заключена папка", "move_to_locked_folder_confirmation": "Тези снимки и видеа ще бъдат изтрити от всички албуми и ще са достъпни само в заключената папка", "moved_to_archive": "{count, plural, one {# обект е преместен} many {# обекта са преместени} other {# обекта са преместени}} в архива", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Премахни зададения диапазон от дати", "remove_deleted_assets": "Премахни Изтритите Елементи", "remove_from_album": "Премахни от албума", + "remove_from_album_action_prompt": "{count} са премахнати от албума", "remove_from_favorites": "Премахни от Любими", + "remove_from_lock_folder_action_prompt": "{count} са премахнати от заключената папка", "remove_from_locked_folder": "Махни от заключената папка", "remove_from_locked_folder_confirmation": "Сигурни ли си, че искате тези снимки и видеа да бъдат извадени от заключената папка? Те ще бъдат видими в библиотеката.", "remove_from_shared_link": "Премахни от споделения линк", @@ -1667,6 +1694,7 @@ "settings_saved": "Настройките са запазени", "setup_pin_code": "Задай PIN код", "share": "Споделяне", + "share_action_prompt": "{count} споделени обекта", "share_add_photos": "Добави снимки", "share_assets_selected": "{count} избрани", "share_dialog_preparing": "Подготовка...", @@ -1768,6 +1796,7 @@ "sort_title": "Заглавие", "source": "Код", "stack": "Събери", + "stack_action_prompt": "{count} са групирани", "stack_duplicates": "Подреждане на дубликати", "stack_select_one_photo": "Избери една главна снимка за събраните снимки", "stack_selected_photos": "Подреждане на избрани снимки", @@ -1838,6 +1867,7 @@ "total": "Общо", "total_usage": "Общо използвано", "trash": "Кошче", + "trash_action_prompt": "{count} са преместени в коша", "trash_all": "Изхвърли всички", "trash_count": "В Кошчето {count, number}", "trash_delete_asset": "Вкарай в Кошчето/Изтрий елемент", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Невъзможна промяна на PIN кода", "unable_to_setup_pin_code": "Неуспешно задаване на PIN кода", "unarchive": "Разархивирай", + "unarchive_action_prompt": "{count} са премахнати от Архива", "unarchived_count": "{count, plural, other {Неархивирани #}}", "undo": "Отмени", "unfavorite": "Премахване от любимите", + "unfavorite_action_prompt": "{count} са премахнати от Любими", "unhide_person": "Покажи отново човека", "unknown": "Неизвестно", "unknown_country": "Непозната Държава", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "От маркирай всички дубликати", "unselect_all_in": "Премахни избора на всички от групата {group}", "unstack": "Разкачи", + "unstack_action_prompt": "{count} са разгрупирани", "unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}", + "untagged": "Немаркирани", "up_next": "Следващ", "updated_at": "Обновено", "updated_password": "Паролата е актуализирана", diff --git a/i18n/bn.json b/i18n/bn.json index ed108652b7..d71e4e25ae 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -8,6 +8,7 @@ "actions": "কর্ম", "active": "সচল", "activity": "কার্যকলাপ", + "activity_changed": "একটিভিটি এখন {enabled, select, true {চালু} other {বন্ধ}} আছে", "add": "যোগ করুন", "add_a_description": "একটি বিবরণ যোগ করুন", "add_a_location": "একটি অবস্থান যোগ করুন", @@ -15,5 +16,84 @@ "add_a_title": "একটি শিরোনাম যোগ করুন", "add_endpoint": "এন্ডপয়েন্ট যোগ করুন", "add_exclusion_pattern": "বহির্ভূতকরণ নমুনা", - "add_url": "লিঙ্ক যোগ করুন" + "add_import_path": "ইমপোর্ট করার পাথ যুক্ত করুন", + "add_location": "অবস্থান যুক্ত করুন", + "add_more_users": "আরো ব্যবহারকারী যুক্ত করুন", + "add_partner": "অংশীদার যোগ করুন", + "add_path": "পাথ যুক্ত করুন", + "add_photos": "ছবি যুক্ত করুন", + "add_tag": "ট্যাগ যুক্ত করুন", + "add_to": "যুক্ত করুন…", + "add_to_album": "এলবাম এ যোগ করুন", + "add_to_album_bottom_sheet_added": "{album} এ যোগ করা হয়েছে", + "add_to_album_bottom_sheet_already_exists": "{album} এ আগে থেকেই আছে", + "add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন", + "add_url": "লিঙ্ক যোগ করুন", + "added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে", + "added_to_favorites": "ফেভারিটে যোগ করা হয়েছে", + "added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে", + "admin": { + "add_exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন যোগ করুন। *, **, এবং ? ব্যবহার করে গ্লোবিং করা সম্ভব। \"Raw\" নামের যেকোনো ডিরেক্টরিতে থাকা সমস্ত ফাইল বাদ দিতে \"**/Raw/**\" ব্যবহার করুন। \".tif\" দিয়ে শেষ হওয়া সমস্ত ফাইল বাদ দিতে \"**/*.tif\" ব্যবহার করুন। একটি সম্পূর্ণ পাথ বাদ দিতে, \"/path/to/ignore/**\" ব্যবহার করুন।", + "admin_user": "এডমিন ইউজার", + "asset_offline_description": "এই বহিরাগত লাইব্রেরি সম্পদটি আর ডিস্কে পাওয়া যাচ্ছে না এবং ট্র্যাশে সরানো হয়েছে। যদি ফাইলটি লাইব্রেরির মধ্যে সরানো হয়ে থাকে, তাহলে নতুন সংশ্লিষ্ট সম্পদের জন্য আপনার টাইমলাইন পরীক্ষা করুন। এই সম্পদটি পুনরুদ্ধার করতে, দয়া করে নিশ্চিত করুন যে নীচের ফাইল পাথটি Immich দ্বারা অ্যাক্সেস করা যেতে পারে এবং লাইব্রেরিটি স্ক্যান করুন।", + "authentication_settings": "প্রমাণীকরণ সেটিংস", + "authentication_settings_description": "পাসওয়ার্ড, OAuth এবং অন্যান্য প্রমাণীকরণ সেটিংস পরিচালনা করুন", + "authentication_settings_disable_all": "আপনি কি নিশ্চিত যে আপনি সমস্ত লগইন পদ্ধতি অক্ষম করতে চান? লগইন সম্পূর্ণরূপে অক্ষম করা হবে।", + "authentication_settings_reenable": "পুনরায় সক্ষম করতে, একটি সার্ভার কমান্ড ব্যবহার করুন।", + "background_task_job": "ব্যাকগ্রাউন্ড টাস্ক", + "backup_database": "ডাটাবেস ডাম্প তৈরি করুন", + "backup_database_enable_description": "ডাটাবেস ডাম্প সক্রিয় করুন", + "backup_keep_last_amount": "আগের ডাম্পের পরিমাণ রাখা হবে", + "backup_settings": "ডাটাবেস ডাম্প সেটিংস", + "backup_settings_description": "ডাটাবেস ডাম্প সেটিংস পরিচালনা করুন।", + "cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে", + "config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে", + "confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?", + "confirm_delete_library_assets": "আপনি কি নিশ্চিত যে আপনি এই লাইব্রেরিটি মুছে ফেলতে চান? এটি Immich থেকে {count, plural, one {# contained asset} other {all # contained asset}} মুছে ফেলবে এবং পূর্বাবস্থায় ফেরানো যাবে না। ফাইলগুলি ডিস্কে থাকবে।", + "confirm_email_below": "নিশ্চিত করতে, নিচে \"{email}\" টাইপ করুন", + "confirm_reprocess_all_faces": "আপনি কি নিশ্চিত যে আপনি সমস্ত মুখ পুনরায় প্রক্রিয়া করতে চান? এটি নামযুক্ত ব্যক্তিদেরও মুছে ফেলবে।", + "confirm_user_password_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পাসওয়ার্ড রিসেট করতে চান?", + "confirm_user_pin_code_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পিন কোড রিসেট করতে চান?", + "create_job": "job তৈরি করুন", + "cron_expression": "ক্রোন এক্সপ্রেশন", + "cron_expression_description": "ক্রোন ফর্ম্যাট ব্যবহার করে স্ক্যানিং ব্যবধান সেট করুন। আরও তথ্যের জন্য দয়া করে দেখুন যেমন Crontab Guru", + "cron_expression_presets": "ক্রোন এক্সপ্রেশন প্রিসেট", + "disable_login": "লগইন অক্ষম করুন", + "duplicate_detection_job_description": "অনুরূপ ছবি সনাক্ত করতে সম্পদগুলিতে মেশিন লার্নিং চালান। স্মার্ট অনুসন্ধানের উপর নির্ভর করে", + "exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।", + "external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা", + "face_detection": "মুখ সনাক্তকরণ", + "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখগুলি সনাক্ত করুন। ভিডিওগুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", + "facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।", + "failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}", + "force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।", + "image_format": "ফরম্যাট", + "image_format_description": "WebP JPEG এর তুলনায় ছোট ফাইল তৈরি করে, কিন্তু এনকোড করতে ধীর।", + "image_fullsize_description": "জুম ইন করার সময় ব্যবহৃত স্ট্রিপড মেটাডেটা সহ পূর্ণ আকারের ছবি", + "image_fullsize_enabled": "পূর্ণ-আকারের ছবি তৈরি সক্ষম করুন", + "image_fullsize_enabled_description": "ওয়েব-বান্ধব নয় এমন ফর্ম্যাটের জন্য পূর্ণ-আকারের ছবি তৈরি করুন। \"এমবেডেড প্রিভিউ পছন্দ করুন\" সক্ষম করা থাকলে, রূপান্তর ছাড়াই এমবেডেড প্রিভিউ সরাসরি ব্যবহার করা হয়। JPEG-এর মতো ওয়েব-বান্ধব ফর্ম্যাটগুলিকে প্রভাবিত করে না।", + "image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।", + "image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস", + "image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন", + "image_prefer_embedded_preview_setting_description": "ছবি প্রক্রিয়াকরণের জন্য এবং যখনই উপলব্ধ থাকবে তখন RAW ফটোতে এমবেডেড প্রিভিউ ব্যবহার করুন। এটি কিছু ছবির জন্য আরও সঠিক রঙ তৈরি করতে পারে, তবে প্রিভিউয়ের মান ক্যামেরা-নির্ভর এবং ছবিতে আরও কম্প্রেশন আর্টিফ্যাক্ট থাকতে পারে।", + "image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন", + "image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য ডিসপ্লে P3 ব্যবহার করুন। এটি প্রশস্ত রঙের স্থান সহ ছবির প্রাণবন্ততা আরও ভালভাবে সংরক্ষণ করে, তবে পুরানো ব্রাউজার সংস্করণ সহ পুরানো ডিভাইসগুলিতে ছবিগুলি ভিন্নভাবে প্রদর্শিত হতে পারে। রঙের পরিবর্তন এড়াতে sRGB ছবিগুলিকে sRGB হিসাবে রাখা হয়।", + "image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়", + "image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।", + "image_preview_title": "প্রিভিউ সেটিংস", + "image_quality": "গুণমান", + "image_resolution": "রেজোলিউশন", + "image_resolution_description": "উচ্চ রেজোলিউশনের ক্ষেত্রে আরও বিস্তারিত তথ্য সংরক্ষণ করা সম্ভব কিন্তু এনকোড করতে বেশি সময় লাগে, ফাইলের আকার বড় হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_settings": "চিত্র সেটিংস", + "image_settings_description": "তৈরি করা ছবির মান এবং রেজোলিউশন পরিচালনা করুন", + "image_thumbnail_description": "মেটাডেটা বাদ দেওয়া ছোট থাম্বনেইল, মূল টাইমলাইনের মতো ছবির গ্রুপ দেখার সময় ব্যবহৃত হয়", + "image_thumbnail_quality_description": "থাম্বনেইলের মান ১-১০০। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_thumbnail_title": "থাম্বনেল সেটিংস", + "job_concurrency": "{job} কনকারেন্সি", + "job_created": "Job তৈরি হয়েছে", + "job_not_concurrency_safe": "এই কাজটি সমকালীন-নিরাপদ নয়।", + "job_settings": "কাজের সেটিংস", + "job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন", + "job_status": "চাকরির অবস্থা" + } } diff --git a/i18n/ca.json b/i18n/ca.json index 5c53311a02..39c249c3a3 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -166,6 +166,10 @@ "metadata_settings_description": "Administrar la configuració de les metadades", "migration_job": "Migració", "migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes", + "nightly_tasks_cluster_new_faces_setting": "Agrupa cares noves", + "nightly_tasks_database_cleanup_setting": "Tasques de neteja de la base de dades", + "nightly_tasks_database_cleanup_setting_description": "Netegeu les dades antigues i caducades de la base de dades", + "nightly_tasks_missing_thumbnails_setting": "Generar les miniatures restants", "no_paths_added": "No s'ha afegit cap ruta", "no_pattern_added": "Cap patró aplicat", "note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la", @@ -196,6 +200,8 @@ "oauth_mobile_redirect_uri": "URI de redirecció mòbil", "oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil", "oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''", + "oauth_role_claim": "Concessió de rol", + "oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth", "oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la documentació.", @@ -244,6 +250,7 @@ "storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la {job}.", "storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge", "storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la Storage Template i les seves implications", + "storage_template_onboarding_description_v2": "Un cop habilitada, aquesta funció organitzarà automàticament els fitxers a partir d'una plantilla definida per l'usuari. Per a més informació, podeu consultar la documentació.", "storage_template_path_length": "Límit aproximat de longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla d'emmagatzematge", "storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats", @@ -359,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.", "advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius", "advanced_settings_log_level_title": "Nivell de registre: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements locals. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", "advanced_settings_prefer_remote_title": "Prefereix imatges remotes", "advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa", "advanced_settings_proxy_headers_title": "Capçaleres de proxy", @@ -426,6 +433,7 @@ "app_settings": "Configuració de l'app", "appears_in": "Apareix a", "archive": "Arxiu", + "archive_action_prompt": "{count} afegit a Arxiu", "archive_or_unarchive_photo": "Arxivar o desarxivar fotografia", "archive_page_no_archived_assets": "No s'ha trobat res arxivat", "archive_page_title": "Arxiu({count})", @@ -463,7 +471,6 @@ "assets": "Elements", "assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}", "assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum", - "assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", "assets_deleted_permanently": "{count} element(s) esborrats permanentment", @@ -702,7 +709,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Fosc", - "darkTheme": "Activa/desactiva el tema fosc", + "dark_theme": "Canviar a tema fosc", "date_after": "Data posterior a", "date_and_time": "Data i hora", "date_before": "Data anterior a", @@ -718,6 +725,7 @@ "default_locale": "Localització predeterminada", "default_locale_description": "Format de dates i números segons la configuració del navegador", "delete": "Esborra", + "delete_action_prompt": "{count} eliminats permanentment", "delete_album": "Esborra l'àlbum", "delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?", "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu", @@ -798,6 +806,7 @@ "edit_key": "Edita clau", "edit_link": "Edita enllaç", "edit_location": "Edita ubicació", + "edit_location_action_prompt": "{count} ubicacions editades", "edit_location_dialog_title": "Ubicació", "edit_name": "Edita el nom", "edit_people": "Edita la gent", @@ -983,6 +992,7 @@ "failed_to_load_assets": "Error carregant recursos", "failed_to_load_folder": "No s'ha pogut carregar la carpeta", "favorite": "Preferit", + "favorite_action_prompt": "{count} afegit a Favorits", "favorite_or_unfavorite_photo": "Foto preferida o no preferida", "favorites": "Preferits", "favorites_page_no_favorites": "No s'han trobat preferits", @@ -1149,6 +1159,7 @@ "locked_folder": "Carpeta bloquejada", "log_out": "Tanca la sessió", "log_out_all_devices": "Tanqueu la sessió de tots els dispositius", + "logged_in_as": "Sessió iniciada com a {user}", "logged_out_all_devices": "S'ha tancat la sessió de tots els dispositius", "logged_out_device": "Dispositiu tancat", "login": "Iniciar sessió", @@ -1244,6 +1255,7 @@ "more": "Més", "move": "Moure", "move_off_locked_folder": "Moure fora de la carpeta bloquejada", + "move_to_lock_folder_action_prompt": "{count} afegides a la carpeta protegida", "move_to_locked_folder": "Moure a la carpeta bloquejada", "move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i només podran ser vistes des de la carpeta bloquejada", "moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu", @@ -1494,6 +1506,7 @@ "remove_deleted_assets": "Suprimeix fitxers fora de línia", "remove_from_album": "Treu de l'àlbum", "remove_from_favorites": "Eliminar dels preferits", + "remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida", "remove_from_locked_folder": "Elimina de la carpeta bloquejada", "remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.", "remove_from_shared_link": "Eliminar de l'enllaç compartit", @@ -1606,6 +1619,7 @@ "select_album_cover": "Seleccionar la portada de l'àlbum", "select_all": "Selecciona-ho tot", "select_all_duplicates": "Seleccioneu tots els duplicats", + "select_all_in": "Selecciona tot en {group}", "select_avatar_color": "Tria color de l'avatar", "select_face": "Selecciona cara", "select_featured_photo": "Selecciona foto principal", @@ -1835,6 +1849,7 @@ "total": "Total", "total_usage": "Ús total", "trash": "Paperera", + "trash_action_prompt": "{count} mogudes a la brossa", "trash_all": "Envia-ho tot a la paperera", "trash_count": "Paperera {count, number}", "trash_delete_asset": "Esborra/Elimina element", @@ -1852,9 +1867,11 @@ "unable_to_change_pin_code": "No es pot canviar el codi PIN", "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", + "unarchive_action_prompt": "{count} eliminades de l'arxiu", "unarchived_count": "{count, plural, other {# elements desarxivats}}", "undo": "Desfer", "unfavorite": "Reverteix preferit", + "unfavorite_action_prompt": "{count} eliminades de preferits", "unhide_person": "Mostra persona", "unknown": "Desconegut", "unknown_country": "País Desconegut", @@ -1870,6 +1887,7 @@ "unsaved_change": "Canvi no desat", "unselect_all": "Deselecciona-ho tot", "unselect_all_duplicates": "Desmarqueu tots els duplicats", + "unselect_all_in": "Desseleccionar tots els elements de {group}", "unstack": "Desapila", "unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}", "up_next": "Pròxim", diff --git a/i18n/cs.json b/i18n/cs.json index 82e72cab46..c14a4b6d0c 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -44,7 +44,7 @@ "backup_database": "Vytvořit výpis databáze", "backup_database_enable_description": "Povolit výpisy z databáze", "backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat", - "backup_settings": "Nastavení výpisu databáze", + "backup_settings": "Zálohování databáze", "backup_settings_description": "Správa nastavení výpisu databáze.", "cleared_jobs": "Hotové úlohy pro: {job}", "config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem", @@ -166,6 +166,20 @@ "metadata_settings_description": "Správa nastavení metadat", "migration_job": "Migrace", "migration_job_description": "Migrace miniatur snímků a obličejů do nejnovější struktury složek", + "nightly_tasks_cluster_faces_setting_description": "Spustit rozpoznávání obličeje na nově nalezených obličejích", + "nightly_tasks_cluster_new_faces_setting": "Seskupit nové tváře", + "nightly_tasks_database_cleanup_setting": "Úlohy čištění databáze", + "nightly_tasks_database_cleanup_setting_description": "Vyčistit databázi od starých dat, jejichž platnost vypršela", + "nightly_tasks_generate_memories_setting": "Vytváření vzpomínek", + "nightly_tasks_generate_memories_setting_description": "Vytváření nových vzpomínek z položek", + "nightly_tasks_missing_thumbnails_setting": "Generovat chybějící miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Řadit položky bez miniatur do fronty pro generování miniatur", + "nightly_tasks_settings": "Noční úlohy", + "nightly_tasks_settings_description": "Správa nočních úkolů", + "nightly_tasks_start_time_setting": "Čas zahájení", + "nightly_tasks_start_time_setting_description": "Čas, kdy server spustí noční úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizace využití kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovat kvótu úložiště uživatele na základě aktuálního využití", "no_paths_added": "Nebyly přidány žádné cesty", "no_pattern_added": "Nebyl přidán žádný vzor", "note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilní přesměrování URI", "oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI", "oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''", + "oauth_role_claim": "Deklarace Role", + "oauth_role_claim_description": "Automaticky udělit přístup správce na základě přítomnosti této deklarace. Deklarace může mít hodnotu 'user' nebo 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Správa nastavení OAuth přihlášení", "oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v dokumentaci.", @@ -357,10 +373,12 @@ "admin_password": "Heslo správce", "administration": "Administrace", "advanced": "Pokročilé", + "advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace", + "advanced_settings_beta_timeline_title": "Časová osa beta verze", "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení", "advanced_settings_log_level_title": "Úroveň protokolování: {level}", - "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", + "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z lokálních prostředků velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", "advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky", "advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem", "advanced_settings_proxy_headers_title": "Proxy hlavičky", @@ -388,6 +406,7 @@ "album_options": "Možnosti alba", "album_remove_user": "Odebrat uživatele?", "album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?", + "album_search_not_found": "Nebyla nalezena žádná alba odpovídající vašemu hledání", "album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.", "album_updated": "Album aktualizováno", "album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Výchozí řazení alb", "albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.", "albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.", + "albums_on_device_count": "Alba v zařízení ({count})", "all": "Vše", "all_albums": "Všechna alba", "all_people": "Všichni lidé", @@ -427,7 +447,8 @@ "app_settings": "Aplikace", "appears_in": "Vyskytuje se v", "archive": "Archiv", - "archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku", + "archive_action_prompt": "{count} přidaných do archivu", + "archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu", "archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média", "archive_page_title": "Archiv ({count})", "archive_size": "Velikost archivu", @@ -464,14 +485,13 @@ "assets": "Položky", "assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}", "assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}", - "assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba {name}} other {nového alba}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}", "assets_deleted_permanently": "{count} položek trvale odstraněno", "assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru", "assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}", "assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}", - "assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}", + "assets_moved_to_trash_count": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do koše", "assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}", "assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}", "assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení", @@ -587,6 +607,7 @@ "cancel": "Zrušit", "cancel_search": "Zrušit vyhledávání", "canceled": "Zrušeno", + "canceling": "Rušení", "cannot_merge_people": "Nelze sloučit osoby", "cannot_undo_this_action": "Tuto akci nelze vrátit zpět!", "cannot_update_the_description": "Nelze aktualizovat popis", @@ -703,7 +724,7 @@ "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", - "darkTheme": "Přepnout tmavý motiv", + "dark_theme": "Přepnout tmavý motiv", "date_after": "Datum po", "date_and_time": "Datum a čas", "date_before": "Datum před", @@ -719,6 +740,7 @@ "default_locale": "Výchozí jazyk", "default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče", "delete": "Smazat", + "delete_action_prompt": "{count} trvale smazaných", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení", @@ -732,6 +754,7 @@ "delete_key": "Smazat klíč", "delete_library": "Smazat knihovnu", "delete_link": "Smazat odkaz", + "delete_local_action_prompt": "{count} smazáno lokálně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované", "delete_local_dialog_ok_force": "Přesto smazat", "delete_others": "Odstranit ostatní", @@ -745,6 +768,7 @@ "description": "Popis", "description_input_hint_text": "Přidat popis...", "description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu", + "deselect_all": "Zrušit výběr všech", "details": "Podrobnosti", "direction": "Směr", "disabled": "Zakázáno", @@ -762,6 +786,7 @@ "documentation": "Dokumentace", "done": "Hotovo", "download": "Stáhnout", + "download_action_prompt": "Stahování {count} položek", "download_canceled": "Stahování zrušeno", "download_complete": "Stahování kompletní", "download_enqueue": "Stahování ve frontě", @@ -799,6 +824,7 @@ "edit_key": "Upravit klíč", "edit_link": "Upravit odkaz", "edit_location": "Upravit polohu", + "edit_location_action_prompt": "{count} upravených poloh", "edit_location_dialog_title": "Poloha", "edit_name": "Upravit jméno", "edit_people": "Upravit lidi", @@ -817,6 +843,7 @@ "empty_trash": "Vyprázdnit koš", "empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!", "enable": "Povolit", + "enable_backup": "Povolit zálohování", "enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování", "enabled": "Povoleno", "end_date": "Konečné datum", @@ -861,7 +888,7 @@ "failed_to_load_people": "Chyba načítání osob", "failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu", "failed_to_stack_assets": "Nepodařilo se seskupit položky", - "failed_to_unstack_assets": "Nepodařilo se rozložit položky", + "failed_to_unstack_assets": "Nepodařilo se zrušit seskupení položek", "failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení", "import_path_already_exists": "Tato cesta importu již existuje.", "incorrect_email_or_password": "Nesprávný e-mail nebo heslo", @@ -876,7 +903,7 @@ "unable_to_add_partners": "Nelze přidat partnery", "unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit položku z} other {přidat položku do}} archivu", "unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit položku} other {zrušit oblíbení položky}}", - "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}", + "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odebrat z archivu}}", "unable_to_change_album_user_role": "Nelze změnit roli uživatele alba", "unable_to_change_date": "Nelze změnit datum", "unable_to_change_description": "Nelze změnit popis", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Nepodařilo se načíst položky", "failed_to_load_folder": "Nepodařilo se načíst složku", "favorite": "Oblíbit", + "favorite_action_prompt": "{count} přidáno do Oblíbených", "favorite_or_unfavorite_photo": "Oblíbit nebo zrušit oblíbení fotky", "favorites": "Oblíbené", "favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Naposledy vytvořené", "library_page_sort_last_modified": "Naposledy upraveno", "library_page_sort_title": "Podle názvu alba", + "licenses": "Licence", "light": "Světlý", "like_deleted": "Lajk smazán", "link_motion_video": "Připojit pohyblivé video", @@ -1246,10 +1275,11 @@ "more": "Více", "move": "Přesunout", "move_off_locked_folder": "Přesunout z uzamčené složky", + "move_to_lock_folder_action_prompt": "{count} přidaných do uzamčené složky", "move_to_locked_folder": "Přesunout do uzamčené složky", "move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce", - "moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu", - "moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny", + "moved_to_archive": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do archivu", + "moved_to_library": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do knihovny", "moved_to_trash": "Přesunuto do koše", "multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji", "multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Stav podporovatele", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový klíč serveru spravuje správce", + "queue_status": "Ve frontě {count}/{total}", "rating": "Hodnocení hvězdičkami", "rating_clear": "Vyčistit hodnocení", "rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Odstranit vlastní rozsah datumů", "remove_deleted_assets": "Odstranit offline soubory", "remove_from_album": "Odstranit z alba", + "remove_from_album_action_prompt": "{count} odstraněných z alba", "remove_from_favorites": "Odstranit z oblíbených", + "remove_from_lock_folder_action_prompt": "{count} odebraných z uzamčené složky", "remove_from_locked_folder": "Odstranit z uzamčené složky", "remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.", "remove_from_shared_link": "Odstranit ze sdíleného odkazu", @@ -1667,6 +1700,7 @@ "settings_saved": "Nastavení uloženo", "setup_pin_code": "Nastavení PIN kódu", "share": "Sdílet", + "share_action_prompt": "Sdíleno {count} položek", "share_add_photos": "Přidat fotografie", "share_assets_selected": "{count} vybráno", "share_dialog_preparing": "Připravuji...", @@ -1768,6 +1802,7 @@ "sort_title": "Název alba", "source": "Zdroj", "stack": "Seskupit", + "stack_action_prompt": "{count} seskupeno", "stack_duplicates": "Seskupit duplicity", "stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení", "stack_selected_photos": "Seskupení vybraných fotografií", @@ -1838,6 +1873,7 @@ "total": "Celkem", "total_usage": "Celkové využití", "trash": "Koš", + "trash_action_prompt": "{count} přesunutých do koše", "trash_all": "Vyhodit vše", "trash_count": "Vyhodit {count, number}", "trash_delete_asset": "Vyhodit/Smazat položku", @@ -1854,10 +1890,12 @@ "type": "Typ", "unable_to_change_pin_code": "Nelze změnit PIN kód", "unable_to_setup_pin_code": "Nelze nastavit PIN kód", - "unarchive": "Odarchivovat", + "unarchive": "Odebrat z archivu", + "unarchive_action_prompt": "{count} odstraněných z archivu", "unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}", "undo": "Vrátit zpět", "unfavorite": "Zrušit oblíbení", + "unfavorite_action_prompt": "{count} odstraněných z oblíbených", "unhide_person": "Zrušit skrytí osoby", "unknown": "Neznámý", "unknown_country": "Neznámá země", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Zrušit výběr všech duplicit", "unselect_all_in": "Zrušit výběr ve skupině {group}", "unstack": "Zrušit seskupení", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} seskupených zrušeno", + "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}", + "untagged": "Neoznačeno", "up_next": "To je prozatím vše", "updated_at": "Aktualizováno", "updated_password": "Heslo aktualizováno", "upload": "Nahrát", "upload_concurrency": "Souběžnost nahrávání", + "upload_details": "Detaily nahrávání", "upload_dialog_info": "Chcete zálohovat vybrané položky na server?", "upload_dialog_title": "Nahrát položku", "upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Zobrazit statistiky používání účtu", "username": "Uživateleské jméno", "users": "Uživatelé", + "users_added_to_album_count": "{count, plural, one {Přidán # uživatel} few {Přidány # uživatelé} other {Přidáno # uživatelů}} do alba", "utilities": "Nástroje", "validate": "Ověřit", "validate_endpoint_error": "Zadejte platné URL", @@ -1930,6 +1972,7 @@ "view_album": "Zobrazit album", "view_all": "Zobrazit vše", "view_all_users": "Zobrazit všechny uživatele", + "view_details": "Zobrazit podrobnosti", "view_in_timeline": "Zobrazit na časové ose", "view_link": "Zobrazit odkaz", "view_links": "Zobrazit odkazy", @@ -1941,7 +1984,7 @@ "view_user": "Zobrazit uživatele", "viewer_remove_from_stack": "Odstranit ze zásobníku", "viewer_stack_use_as_main_asset": "Použít jako hlavní položku", - "viewer_unstack": "Rozbalit zásobník", + "viewer_unstack": "Zrušit zásobník", "visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}", "waiting": "Čekající", "warning": "Upozornění", diff --git a/i18n/da.json b/i18n/da.json index 3273a2d553..e9f9534927 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Tilføjet {count, number} til favoritter", "admin": { "add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjælp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter på \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".", + "admin_user": "Administrator bruger", "asset_offline_description": "Denne eksterne biblioteksressource findes ikke længere på disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgås af Immich og scanne biblioteket.", "authentication_settings": "Godkendelsesindstillinger", "authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger", @@ -165,6 +166,7 @@ "metadata_settings_description": "Håndtér metadataindstillinger", "migration_job": "Migrering", "migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", + "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør", @@ -462,7 +464,6 @@ "assets": "elementer", "assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}", "assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet", - "assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {{name}} other {nyt album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", @@ -701,7 +702,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mørk", - "darkTheme": "Skift til mørkt tema", "date_after": "Dato efter", "date_and_time": "Dato og klokkeslæt", "date_before": "Dato før", diff --git a/i18n/de.json b/i18n/de.json index bbcd9c569c..79ba1251d5 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -56,9 +56,9 @@ "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?", "create_job": "Aufgabe erstellen", "cron_expression": "Cron Zeitangabe", - "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der Crontab Guru", + "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", "cron_expression_presets": "Nützliche Zeitangaben für Cron", - "disable_login": "Login deaktvieren", + "disable_login": "Login deaktivieren", "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.", "external_library_management": "Verwaltung externer Bibliotheken", @@ -166,6 +166,20 @@ "metadata_settings_description": "Metadaten-Einstellungen verwalten", "migration_job": "Migration", "migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur", + "nightly_tasks_cluster_faces_setting_description": "Gesichtsidentifikation auf neu erkannten Gesichtern ausführen", + "nightly_tasks_cluster_new_faces_setting": "Neue Gesichter gruppieren", + "nightly_tasks_database_cleanup_setting": "Datenbankbereinigungs-Aufgaben", + "nightly_tasks_database_cleanup_setting_description": "Alte, abgelaufene Daten aus der Datenbank bereinigen", + "nightly_tasks_generate_memories_setting": "Erinnerungen generieren", + "nightly_tasks_generate_memories_setting_description": "Neue Erinnerungen aus Dateien erstellen", + "nightly_tasks_missing_thumbnails_setting": "Fehlende Miniaturansichten generieren", + "nightly_tasks_missing_thumbnails_setting_description": "Dateien ohne Miniaturansicht in die Warteschlange zur Miniaturansicht-Generierung hinzufügen", + "nightly_tasks_settings": "Einstellungen für nächtliche Aufgaben", + "nightly_tasks_settings_description": "Nächtliche Aufgaben verwalten", + "nightly_tasks_start_time_setting": "Startzeit", + "nightly_tasks_start_time_setting_description": "Die Zeit, zu der der Server mit der Ausführung der nächtlichen Aufgaben beginnt", + "nightly_tasks_sync_quota_usage_setting": "Kontingentnutzung synchronisieren", + "nightly_tasks_sync_quota_usage_setting_description": "Benutzerspeicherkontingent basierend auf der aktuellen Nutzung aktualisieren", "no_paths_added": "Keine Pfade hinzugefügt", "no_pattern_added": "Kein Ausschlussmuster hinzugefügt", "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobile Umleitungs-URI", "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt", + "oauth_role_claim": "Rollen-Claim", + "oauth_role_claim_description": "Gewähre automatisch Admin-Zugriff basierend auf dem Vorhandensein dieses Claims. Der Claim kann entweder 'user' oder 'admin' sein.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", @@ -244,6 +260,7 @@ "storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den {job} aus.", "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", + "storage_template_onboarding_description_v2": "Wenn aktiviert, werden Dateien automatisch nach einer benutzerdefinierten Vorlage organisiert. Für mehr Informationen siehe die Dokumentation.", "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", @@ -356,10 +373,12 @@ "admin_password": "Administrator Passwort", "administration": "Verwaltung", "advanced": "Erweitert", + "advanced_settings_beta_timeline_subtitle": "Probier die neue App-Erfahrung aus", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben", "advanced_settings_log_level_title": "Log-Level: {level}", - "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", + "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von lokalen Vorschaubildern. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", "advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen", "advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll", "advanced_settings_proxy_headers_title": "Proxy-Headers", @@ -387,6 +406,7 @@ "album_options": "Albumoptionen", "album_remove_user": "Nutzer entfernen?", "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", + "album_search_not_found": "Keine Alben gefunden, die zur Suche passen", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", "album_updated": "Album aktualisiert", "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", @@ -406,6 +426,7 @@ "albums_default_sort_order": "Standard Album Sortierung", "albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.", "albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden können.", + "albums_on_device_count": "Alben auf dem Gerät ({count})", "all": "Alle", "all_albums": "Alle Alben", "all_people": "Alle Personen", @@ -426,6 +447,7 @@ "app_settings": "App-Einstellungen", "appears_in": "Erscheint in", "archive": "Archiv", + "archive_action_prompt": "{count} zum Archiv hinzugefügt", "archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben", "archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden", "archive_page_title": "Archiv ({count})", @@ -463,7 +485,6 @@ "assets": "Dateien", "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt", "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt", - "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefügt", "assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_deleted_permanently": "{count} Element(e) permanent gelöscht", @@ -586,6 +607,7 @@ "cancel": "Abbrechen", "cancel_search": "Suche abbrechen", "canceled": "Abgebrochen", + "canceling": "Abbrechen", "cannot_merge_people": "Personen können nicht zusammengeführt werden", "cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", @@ -702,7 +724,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", - "darkTheme": "Dunkles Theme umschalten", + "dark_theme": "Dunkle Ansicht umschalten", "date_after": "Datum nach", "date_and_time": "Datum und Zeit", "date_before": "Datum vor", @@ -718,6 +740,7 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "Löschen", + "delete_action_prompt": "{count} endgültig gelöscht", "delete_album": "Album löschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", @@ -731,6 +754,7 @@ "delete_key": "Schlüssel löschen", "delete_library": "Bibliothek löschen", "delete_link": "Link löschen", + "delete_local_action_prompt": "{count} lokal gelöscht", "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen", "delete_local_dialog_ok_force": "Trotzdem löschen", "delete_others": "Andere löschen", @@ -744,6 +768,7 @@ "description": "Beschreibung", "description_input_hint_text": "Beschreibung hinzufügen...", "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen", + "deselect_all": "Alle abwählen", "details": "Details", "direction": "Richtung", "disabled": "Deaktiviert", @@ -761,6 +786,7 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", + "download_action_prompt": "Herunterladen von {count} Dateien", "download_canceled": "Download abgebrochen", "download_complete": "Download vollständig", "download_enqueue": "Download in die Warteschlange gesetzt", @@ -798,6 +824,7 @@ "edit_key": "Schlüssel bearbeiten", "edit_link": "Link bearbeiten", "edit_location": "Standort bearbeiten", + "edit_location_action_prompt": "{count} Geolokationen angepasst", "edit_location_dialog_title": "Ort bearbeiten", "edit_name": "Name bearbeiten", "edit_people": "Personen bearbeiten", @@ -816,6 +843,7 @@ "empty_trash": "Papierkorb leeren", "empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!", "enable": "Aktivieren", + "enable_backup": "Sicherung aktivieren", "enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren", "enabled": "Aktiviert", "end_date": "Enddatum", @@ -983,6 +1011,7 @@ "failed_to_load_assets": "Laden der Assets fehlgeschlagen", "failed_to_load_folder": "Fehler beim Laden des Ordners", "favorite": "Favorit", + "favorite_action_prompt": "{count} zu den Favoriten hinzugefügt", "favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto", "favorites": "Favoriten", "favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden", @@ -1126,6 +1155,7 @@ "library_page_sort_created": "Zuletzt erstellt", "library_page_sort_last_modified": "Zuletzt bearbeitet", "library_page_sort_title": "Titel des Albums", + "licenses": "Lizenzen", "light": "Hell", "like_deleted": "Like gelöscht", "link_motion_video": "Bewegungsvideo verknüpfen", @@ -1245,6 +1275,7 @@ "more": "Mehr", "move": "Verschieben", "move_off_locked_folder": "Aus dem gesperrten Ordner verschieben", + "move_to_lock_folder_action_prompt": "{count} zum gesperrten Ordner hinzugefügt", "move_to_locked_folder": "In den gesperrten Ordner verschieben", "move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und können nur noch im gesperrten Ordner angezeigt werden", "moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert", @@ -1459,6 +1490,7 @@ "purchase_server_description_2": "Unterstützerstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet", + "queue_status": "Warteschlange {count}/{total}", "rating": "Bewertung", "rating_clear": "Bewertung löschen", "rating_count": "{count, plural, one {# Stern} other {# Sterne}}", @@ -1494,7 +1526,9 @@ "remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen", "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", + "remove_from_album_action_prompt": "{count} vom Album entfernt", "remove_from_favorites": "Aus Favoriten entfernen", + "remove_from_lock_folder_action_prompt": "{count} aus dem gesperrten Ordner entfernt", "remove_from_locked_folder": "Aus gesperrtem Ordner entfernen", "remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen möchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.", "remove_from_shared_link": "Aus geteiltem Link entfernen", @@ -1666,6 +1700,7 @@ "settings_saved": "Einstellungen gespeichert", "setup_pin_code": "Einen PIN Code festlegen", "share": "Teilen", + "share_action_prompt": "{count} Dateien geteilt", "share_add_photos": "Fotos hinzufügen", "share_assets_selected": "{count} ausgewählt", "share_dialog_preparing": "Vorbereiten...", @@ -1767,6 +1802,7 @@ "sort_title": "Titel", "source": "Quellcode", "stack": "Stapel", + "stack_action_prompt": "{count} gestapelt", "stack_duplicates": "Duplikate stapeln", "stack_select_one_photo": "Hauptfoto für den Stapel auswählen", "stack_selected_photos": "Ausgewählte Fotos stapeln", @@ -1837,6 +1873,7 @@ "total": "Gesamt", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", + "trash_action_prompt": "{count} in den Papierkorb verschoben", "trash_all": "Alle löschen", "trash_count": "Papierkorb {count, number}", "trash_delete_asset": "Datei löschen/in den Papierkorb verschieben", @@ -1854,9 +1891,11 @@ "unable_to_change_pin_code": "PIN Code konnte nicht geändert werden", "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", + "unarchive_action_prompt": "{count} aus dem Archiv entfernt", "unarchived_count": "{count, plural, other {# entarchiviert}}", "undo": "Rückgängig", "unfavorite": "Entfavorisieren", + "unfavorite_action_prompt": "{count} aus den Favoriten entfernt", "unhide_person": "Person einblenden", "unknown": "Unbekannt", "unknown_country": "Unbekanntes Land", @@ -1874,12 +1913,15 @@ "unselect_all_duplicates": "Alle Duplikate abwählen", "unselect_all_in": "Alle in {group} abwählen", "unstack": "Entstapeln", + "unstack_action_prompt": "{count} entstapelt", "unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt", + "untagged": "Ohne Tag", "up_next": "Weiter", "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", "upload_concurrency": "Parallelität beim Hochladen", + "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", @@ -1911,6 +1953,7 @@ "user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen", "username": "Nutzername", "users": "Benutzer", + "users_added_to_album_count": "{count, plural, one {# Benutzer} other {# Benutzer}} zum Album hinzugefügt", "utilities": "Hilfsmittel", "validate": "Validieren", "validate_endpoint_error": "Bitte gib eine gültige URL ein", @@ -1929,6 +1972,7 @@ "view_album": "Album anzeigen", "view_all": "Alles anzeigen", "view_all_users": "Alle Nutzer anzeigen", + "view_details": "Details ansehen", "view_in_timeline": "In Zeitleiste anzeigen", "view_link": "Link anzeigen", "view_links": "Links anzeigen", diff --git a/i18n/el.json b/i18n/el.json index 62d0481ec6..015bf80c0a 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -464,7 +464,6 @@ "assets": "Αντικείμενα", "assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}", "assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ", - "assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {{name}} other {νέο άλμπουμ}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Στοιχείο} other {Στοιχεία}} δεν μπορούν να προστεθούν στο άλμπουμ", "assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}", "assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά", @@ -703,7 +702,6 @@ "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "Σκούρο", - "darkTheme": "Εναλλαγή σκούρου θέματος", "date_after": "Ημερομηνία μετά", "date_and_time": "Ημερομηνία και ώρα", "date_before": "Ημερομηνία πριν", diff --git a/i18n/es.json b/i18n/es.json index 7bfdc678df..83d1c9d355 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -2,7 +2,7 @@ "about": "Acerca de", "account": "Cuenta", "account_settings": "Ajustes de la cuenta", - "acknowledge": "De acuerdo", + "acknowledge": "Aceptar", "action": "Acción", "action_common_update": "Actualizar", "actions": "Acciones", @@ -14,13 +14,13 @@ "add_a_location": "Agregar ubicación", "add_a_name": "Agregar nombre", "add_a_title": "Agregar título", - "add_endpoint": "Añadir endpoint", + "add_endpoint": "Agregar endpoint", "add_exclusion_pattern": "Agregar patrón de exclusión", "add_import_path": "Agregar ruta de importación", "add_location": "Agregar ubicación", "add_more_users": "Agregar más usuarios", "add_partner": "Agregar compañero", - "add_path": "Agregar carpeta", + "add_path": "Agregar ruta", "add_photos": "Agregar fotos", "add_tag": "Agregar etiqueta", "add_to": "Agregar a…", @@ -63,8 +63,8 @@ "exclusion_pattern_description": "Los patrones de exclusión te permiten ignorar archivos y carpetas al escanear tu biblioteca. Es útil si tienes carpetas que contienen archivos que no deseas importar, por ejemplo archivos RAW.", "external_library_management": "Gestión de bibliotecas externas", "face_detection": "Detección de caras", - "face_detection_description": "Detecta las caras en los activos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Falta\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", - "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se ejecuta una vez finalizada la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Falta\" pone en cola las caras que no tienen asignada una persona.", + "face_detection_description": "Detecta las caras en los elementos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Faltante\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", + "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se realiza después de completar la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Faltante\" pone en cola las caras que no tienen una persona asignada.", "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "force_delete_user_warning": "CUIDADO: Esta acción eliminará inmediatamente el usuario y todos los elementos. Esta accion no se puede deshacer y los archivos no pueden ser recuperados.", "image_format": "Formato", @@ -166,6 +166,20 @@ "metadata_settings_description": "Administrar la configuración de metadatos", "migration_job": "Migración", "migration_job_description": "Migrar miniaturas de archivos y caras a la estructura de carpetas más reciente", + "nightly_tasks_cluster_faces_setting_description": "Ejecutar reconocimiento facial en caras detectadas recientemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar caras nuevas", + "nightly_tasks_database_cleanup_setting": "Tareas de limpieza de base de datos", + "nightly_tasks_database_cleanup_setting_description": "Limpiar datos antiguos y caducados de la base de datos", + "nightly_tasks_generate_memories_setting": "Generar recuerdos", + "nightly_tasks_generate_memories_setting_description": "Crear nuevos recuerdos a partir de activos", + "nightly_tasks_missing_thumbnails_setting": "Generar miniaturas faltantes", + "nightly_tasks_missing_thumbnails_setting_description": "Poner en cola a activos sin miniaturas para la generación de miniaturas", + "nightly_tasks_settings": "Configuración de Tareas Nocturnas", + "nightly_tasks_settings_description": "Gestionar Tareas Nocturnas", + "nightly_tasks_start_time_setting": "Tiempo de inicio", + "nightly_tasks_start_time_setting_description": "El tiempo cuando el servidor comienza a ejecutar las tareas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Uso de la cuota de sincronización", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, según el uso actual", "no_paths_added": "No se han añadido carpetas", "no_pattern_added": "No se han añadido patrones", "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redireccionamiento móvil", "oauth_mobile_redirect_uri_override": "Sobreescribir URI de redirección móvil", "oauth_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI móvil, como ''{callback}''", + "oauth_role_claim": "Concesión de rol", + "oauth_role_claim_description": "Otorgar acceso de administrador automáticamente según la presencia de esta concesión. La concesión puede tener \"usuario\" o \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Administrar la configuración de inicio de sesión de OAuth", "oauth_settings_more_details": "Para más detalles acerca de esta característica, consulte la documentación.", @@ -205,7 +221,7 @@ "oauth_storage_quota_claim_description": "Establezca automáticamente la cuota de almacenamiento del usuario al valor de esta solicitud.", "oauth_storage_quota_default": "Cuota de almacenamiento predeterminada (GiB)", "oauth_storage_quota_default_description": "Cuota en GiB que se utilizará cuando no se proporcione ninguna por defecto.", - "oauth_timeout": "Expiración de solicitud", + "oauth_timeout": "Límite de tiempo para la solicitud", "oauth_timeout_description": "Tiempo de espera de solicitudes en milisegundos", "password_enable_description": "Iniciar sesión con correo electrónico y contraseña", "password_settings": "Contraseña de Acceso", @@ -357,10 +373,12 @@ "admin_password": "Contraseña del Administrador", "administration": "Administración", "advanced": "Avanzada", + "advanced_settings_beta_timeline_subtitle": "Prueba la nueva experiencia de la aplicación", + "advanced_settings_beta_timeline_title": "Cronología beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opción para filtrar medios durante la sincronización según criterios alternativos. Intenta esto solo si tienes problemas con que la aplicación detecte todos los álbumes.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronización de álbumes del dispositivo", "advanced_settings_log_level_title": "Nivel de registro: {level}", - "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de los elementos encontrados en el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.", + "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los archivos locales. Activa esta opción para cargar imágenes remotas en su lugar.", "advanced_settings_prefer_remote_title": "Preferir imágenes remotas", "advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirá en cada petición de red", "advanced_settings_proxy_headers_title": "Cabeceras Proxy", @@ -405,8 +423,8 @@ "albums": "Álbumes", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbumes}}", "albums_default_sort_order": "Ordenación por defecto de los álbumes", - "albums_default_sort_order_description": "Orden de clasificación inicial de los activos al crear nuevos álbumes.", - "albums_feature_description": "Colecciones de activos que pueden compartirse con otros usuarios.", + "albums_default_sort_order_description": "Orden de clasificación inicial de los recursos al crear nuevos álbumes.", + "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -427,6 +445,7 @@ "app_settings": "Ajustes de Aplicacion", "appears_in": "Aparece en", "archive": "Archivo", + "archive_action_prompt": "{count} añadidos al Archivo", "archive_or_unarchive_photo": "Archivar o restaurar foto", "archive_page_no_archived_assets": "No se encontraron elementos archivados", "archive_page_title": "Archivo ({count})", @@ -464,13 +483,12 @@ "assets": "elementos", "assets_added_count": "Añadido {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Añadido {count, plural, one {# asset} other {# assets}} al álbum", - "assets_added_to_name_count": "Añadido {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no pueden ser añadidos al album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {El recurso no puede ser añadido al álbum} other {Los recursos no pueden ser añadidos al álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich", - "assets_downloaded_failed": "{count, plural, one {Descargado archivo # - {error} archivo fallido} other {Descargados # archivos - {error} archivos fallidos}}", - "assets_downloaded_successfully": "{count, plural, one {Archivo # descargado correctamente} other {Archivos # descargados correctamente}}", + "assets_downloaded_failed": "{count, plural, one {# archivo descargado - {error} archivo fallido} other {# archivos descargados - {error} archivos fallidos}}", + "assets_downloaded_successfully": "{count, plural, one {# archivo descargado exitosamente} other {# archivos descargados exitosamente}}", "assets_moved_to_trash_count": "{count, plural, one {# elemento movido} other {# elementos movidos}} a la papelera", "assets_permanently_deleted_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", "assets_removed_count": "Eliminado {count, plural, one {# elemento} other {# elementos}}", @@ -703,7 +721,7 @@ "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", - "darkTheme": "Activar tema oscuro", + "dark_theme": "Alternar tema oscuro", "date_after": "Fecha posterior", "date_and_time": "Fecha y Hora", "date_before": "Fecha anterior", @@ -719,6 +737,7 @@ "default_locale": "Configuración regional predeterminada", "default_locale_description": "Formatee fechas y números según la configuración regional de su navegador", "delete": "Eliminar", + "delete_action_prompt": "{count} eliminados permanentemente", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serán eliminados permanentemente de Immich y de tu dispositivo", @@ -732,6 +751,7 @@ "delete_key": "Eliminar clave", "delete_library": "Eliminar biblioteca", "delete_link": "Eliminar enlace", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", @@ -749,6 +769,7 @@ "direction": "Dirección", "disabled": "Deshabilitado", "disallow_edits": "Bloquear edición", + "discord": "Discord", "discover": "Descubrir", "discovered_devices": "Dispositivos descubiertos", "dismiss_all_errors": "Descartar todos los errores", @@ -761,6 +782,7 @@ "documentation": "Documentación", "done": "Hecho", "download": "Descargar", + "download_action_prompt": "Descargando {count} archivos", "download_canceled": "Descarga cancelada", "download_complete": "Descarga completada", "download_enqueue": "Descarga en cola", @@ -798,6 +820,7 @@ "edit_key": "Editar clave", "edit_link": "Editar enlace", "edit_location": "Editar ubicación", + "edit_location_action_prompt": "{count} ubicaciones actualizadas", "edit_location_dialog_title": "Ubicación", "edit_name": "Cambiar nombre", "edit_people": "Editar persona", @@ -983,6 +1006,7 @@ "failed_to_load_assets": "Error al cargar los activos", "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", + "favorite_action_prompt": "{count} añadidos a Favoritos", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "No se encontraron elementos marcados como favoritos", @@ -1126,6 +1150,7 @@ "library_page_sort_created": "Creado más recientemente", "library_page_sort_last_modified": "Última modificación", "library_page_sort_title": "Título del álbum", + "licenses": "Licencias", "light": "Claro", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", @@ -1135,7 +1160,7 @@ "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la búsqueda", - "local_asset_cast_failed": "No se puede emitir un activo que no está cargado en el servidor", + "local_asset_cast_failed": "No es posible transmitir un recurso que no está subido al servidor", "local_network": "Red local", "local_network_sheet_info": "La aplicación se conectará al servidor a través de esta URL cuando utilice la red Wi-Fi especificada", "location_permission": "Permiso de ubicación", @@ -1238,13 +1263,14 @@ "merged_people_count": "Fusionada {count, plural, one {# persona} other {# personas}}", "minimize": "Minimizar", "minute": "Minuto", - "missing": "Perdido", + "missing": "Faltante", "model": "Modelo", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM a", "more": "Mas", "move": "Mover", "move_off_locked_folder": "Mover fuera de la carpeta protegida", + "move_to_lock_folder_action_prompt": "{count} añadidos a la carpeta protegida", "move_to_locked_folder": "Mover a la carpeta protegida", "move_to_locked_folder_confirmation": "Estas fotos y vídeos serán eliminados de todos los álbumes y sólo podrán ser vistos desde la carpeta protegida", "moved_to_archive": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a archivo", @@ -1277,7 +1303,7 @@ "no_archived_assets_message": "Archive fotos y videos para ocultarlos de su vista de Fotos", "no_assets_message": "HAZ CLIC PARA SUBIR TU PRIMERA FOTO", "no_assets_to_show": "No hay elementos a mostrar", - "no_cast_devices_found": "Dispositivos de difusión no encontrados", + "no_cast_devices_found": "No se encontraron dispositivos de transmisión", "no_duplicates_found": "No se encontraron duplicados.", "no_exif_info_available": "No hay información exif disponible", "no_explore_results_message": "Sube más fotos para explorar tu colección.", @@ -1494,7 +1520,9 @@ "remove_custom_date_range": "Eliminar intervalo de fechas personalizado", "remove_deleted_assets": "Eliminar archivos sin conexión", "remove_from_album": "Eliminar del álbum", + "remove_from_album_action_prompt": "{count} eliminado del álbum", "remove_from_favorites": "Quitar de favoritos", + "remove_from_lock_folder_action_prompt": "{count} eliminado de la carpeta protegida", "remove_from_locked_folder": "Eliminar de la carpeta protegida", "remove_from_locked_folder_confirmation": "¿Estás seguro de que deseas mover estas fotos y vídeos fuera de la carpeta protegida? Serán visibles en tu biblioteca.", "remove_from_shared_link": "Eliminar desde enlace compartido", @@ -1558,17 +1586,17 @@ "search_city": "Buscar ciudad...", "search_country": "Buscar país...", "search_filter_apply": "Aplicar filtros", - "search_filter_camera_title": "Elige tipo de cámara", + "search_filter_camera_title": "Elegir tipo de cámara", "search_filter_date": "Fecha", "search_filter_date_interval": "{start} al {end}", - "search_filter_date_title": "Selecciona un intervalo de fechas", + "search_filter_date_title": "Seleccionar un intervalo de fechas", "search_filter_display_option_not_in_album": "No en álbum", "search_filter_display_options": "Opciones de visualización", "search_filter_filename": "Buscar por nombre de archivo", "search_filter_location": "Ubicación", "search_filter_location_title": "Seleccionar una ubicación", "search_filter_media_type": "Tipo de archivo", - "search_filter_media_type_title": "Selecciona el tipo de archivo", + "search_filter_media_type_title": "Seleccionar el tipo de archivo", "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", @@ -1591,23 +1619,23 @@ "search_people": "Buscar personas", "search_places": "Buscar lugar", "search_rating": "Buscar por calificación...", - "search_result_page_new_search_hint": "Nueva Busqueda", + "search_result_page_new_search_hint": "Nueva Búsqueda", "search_settings": "Ajustes de la búsqueda", "search_state": "Buscar región/estado...", "search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ", "search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda", - "search_tags": "Buscando etiquetas...", + "search_tags": "Buscar etiquetas...", "search_timezone": "Buscar zona horaria...", "search_type": "Tipo de búsqueda", "search_your_photos": "Busca tus fotos", "searching_locales": "Buscando sitios...", "second": "Segundo", "see_all_people": "Ver todas las personas", - "select": "Selecciona", + "select": "Seleccionar", "select_album_cover": "Seleccionar portada del álbum", "select_all": "Seleccionar todo", "select_all_duplicates": "Seleccionar todos los duplicados", - "select_all_in": "Selecciona todos en {group}", + "select_all_in": "Seleccionar todos en {group}", "select_avatar_color": "Seleccionar color del avatar", "select_face": "Seleccionar cara", "select_featured_photo": "Seleccionar foto principal", @@ -1638,7 +1666,7 @@ "set_date_of_birth": "Establecer fecha de nacimiento", "set_profile_picture": "Establecer foto de perfil", "set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa", - "set_stack_primary_asset": "Establecer como activo principal", + "set_stack_primary_asset": "Establecer como recurso principal", "setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).", "setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).", "setting_image_viewer_original_title": "Cargar imagen original", @@ -1666,6 +1694,7 @@ "settings_saved": "Ajustes guardados", "setup_pin_code": "Establecer un PIN", "share": "Compartir", + "share_action_prompt": "{count} recursos compartidos", "share_add_photos": "Agregar fotos", "share_assets_selected": "{count} seleccionado(s)", "share_dialog_preparing": "Preparando...", @@ -1767,6 +1796,7 @@ "sort_title": "Título", "source": "Origen", "stack": "Apilar", + "stack_action_prompt": "{count} apilados", "stack_duplicates": "Apilar duplicados", "stack_select_one_photo": "Selecciona una imagen principal para la pila", "stack_selected_photos": "Apilar fotos seleccionadas", @@ -1776,7 +1806,7 @@ "start_date": "Fecha de inicio", "state": "Estado", "status": "Estado", - "stop_casting": "Parar difusión", + "stop_casting": "Detener transmisión", "stop_motion_photo": "Parar foto en movimiento", "stop_photo_sharing": "¿Dejar de compartir tus fotos?", "stop_photo_sharing_description": "{partner} ya no podrá acceder a tus fotos.", @@ -1837,6 +1867,7 @@ "total": "Total", "total_usage": "Uso total", "trash": "Papelera", + "trash_action_prompt": "{count} movidos a la papelera", "trash_all": "Descartar todo", "trash_count": "Descartar {count, number}", "trash_delete_asset": "Borrar/Eliminar archivo", @@ -1854,9 +1885,11 @@ "unable_to_change_pin_code": "No se ha podido cambiar el PIN", "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", + "unarchive_action_prompt": "{count} eliminados del archivo", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", "undo": "Deshacer", "unfavorite": "Retirar favorito", + "unfavorite_action_prompt": "{count} eliminados de favoritos", "unhide_person": "Mostrar persona", "unknown": "Desconocido", "unknown_country": "País desconocido", @@ -1874,7 +1907,9 @@ "unselect_all_duplicates": "Deseleccionar todos los duplicados", "unselect_all_in": "Deselecciona todos en {group}", "unstack": "Desapilar", + "unstack_action_prompt": "{count} desapilado(s)", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", + "untagged": "Sin etiqueta", "up_next": "A continuación", "updated_at": "Actualizado", "updated_password": "Contraseña actualizada", @@ -1911,6 +1946,7 @@ "user_usage_stats_description": "Ver estadísticas de uso de la cuenta", "username": "Nombre de usuario", "users": "Usuarios", + "users_added_to_album_count": "{count, plural, one {# usuario agregado} other {# usuarios agregados}} al álbum", "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduce una URL válida", diff --git a/i18n/et.json b/i18n/et.json index a2b17c6138..804d0c6c46 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -33,11 +33,11 @@ "added_to_favorites": "Lisatud lemmikutesse", "added_to_favorites_count": "{count, number} pilti lisatud lemmikutesse", "admin": { - "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide .tif failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/path/to/ignore/**\".", + "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide \".tif\" lõpuga failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/tee/mida/ignoreerida/**\".", "admin_user": "Administraator", - "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht kogu siseselt muutus, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", + "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht muutus kogu siseselt, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", "authentication_settings": "Autentimise seaded", - "authentication_settings_description": "Halda parooli, OAuth ja muid autentimise seadeid", + "authentication_settings_description": "Halda parooli, OAuth'i ja muid autentimise seadeid", "authentication_settings_disable_all": "Kas oled kindel, et soovid kõik sisselogimismeetodid välja lülitada? Sisselogimine lülitatakse täielikult välja.", "authentication_settings_reenable": "Et taas lubada, kasuta serveri käsku.", "background_task_job": "Tausttegumid", @@ -47,26 +47,26 @@ "backup_settings": "Andmebaasi tõmmiste seaded", "backup_settings_description": "Halda andmebaasi tõmmiste seadeid.", "cleared_jobs": "Tööted eemaldatud: {job}", - "config_set_by_file": "Konfiguratsioon on määratud konfifaili abil", + "config_set_by_file": "Konfiguratsioon on määratud konfiguratsioonifaili abil", "confirm_delete_library": "Kas oled kindel, et soovid kustutada {library} kogu?", - "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda ei saa tagasi võtta. Failid jäävad kettale alles.", + "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda toimingut ei saa tagasi võtta. Failid jäävad kettale alles.", "confirm_email_below": "Kinnitamiseks sisesta allpool \"{email}\"", "confirm_reprocess_all_faces": "Kas oled kindel, et soovid kõik näod uuesti töödelda? See eemaldab kõik nimega isikud.", "confirm_user_password_reset": "Kas oled kindel, et soovid kasutaja {user} parooli lähtestada?", "confirm_user_pin_code_reset": "Kas oled kindel, et soovid kasutaja {user} PIN-koodi lähtestada?", "create_job": "Lisa tööde", "cron_expression": "Cron avaldis", - "cron_expression_description": "Sea skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", + "cron_expression_description": "Määra skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", "cron_expression_presets": "Eelseadistatud cron avaldised", "disable_login": "Keela sisselogimine", "duplicate_detection_job_description": "Rakenda üksustele masinõpet, et leida sarnaseid pilte. Kasutab nutiotsingut", - "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", + "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu selle kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", "external_library_management": "Väliste kogude haldus", "face_detection": "Näoavastus", - "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näed. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", + "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näod. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", "facial_recognition_job_description": "Grupeeri avastatud näod inimesteks. See samm käivitub siis, kui näoavastus on lõppenud. \"Lähtesta\" grupeerib kõik näod uuesti. \"Puuduvad\" võtab ette näod, mida pole isikuga seostatud.", "failed_job_command": "Käsk {command} ebaõnnestus töötes: {job}", - "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik üksused. Seda ei saa tagasi võtta ja faile ei saa taastada.", + "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik tema üksused. Toimingut ei saa tagasi võtta ja faile ei saa taastada.", "image_format": "Formaat", "image_format_description": "WebP failid on väiksemad kui JPEG, aga kodeerimine on aeglasem.", "image_fullsize_description": "Täismõõdus pilt ilma metaandmeteta, kasutatakse sisse suumimisel", @@ -77,9 +77,9 @@ "image_prefer_embedded_preview": "Eelista manustatud eelvaadet", "image_prefer_embedded_preview_setting_description": "Kasuta pilditöötluse sisendina võimalusel RAW fotodesse manustatud eelvaateid. See võib mõnede piltide puhul anda tulemuseks täpsemad värvid, aga eelvaate kvaliteet sõltub konkreetsest kaamerast ning pildis võib olla rohkem tihendusmüra.", "image_prefer_wide_gamut": "Eelista laia värvigammat", - "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, aga vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", + "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, kuid vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", "image_preview_description": "Keskmise suurusega pilt ilma metaandmeteta, kasutusel üksiku üksuse vaatamise ja masinõppe jaoks", - "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madala väärtuse seadmine võib mõjutada masinõppe kvaliteeti.", + "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madal väärtus võib mõjutada masinõppe kvaliteeti.", "image_preview_title": "Eelvaate seaded", "image_quality": "Kvaliteet", "image_resolution": "Resolutsioon", @@ -92,7 +92,7 @@ "job_concurrency": "{job} samaaegsus", "job_created": "Tööde lisatud", "job_not_concurrency_safe": "Seda töödet pole ohutu samaaegselt käivitada.", - "job_settings": "Tööte seaded", + "job_settings": "Töödete seaded", "job_settings_description": "Halda töödete samaaegsust", "job_status": "Tööte seisund", "jobs_delayed": "{jobCount, plural, other {# edasi lükatud}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Halda metaandmete seadeid", "migration_job": "Migratsioon", "migration_job_description": "Migreeri üksuste ja nägude pisipildid uusimale kaustastruktuurile", + "nightly_tasks_cluster_faces_setting_description": "Käivita värskelt avastatud nägudel näotuvastus", + "nightly_tasks_cluster_new_faces_setting": "Grupeeri uued näod", + "nightly_tasks_database_cleanup_setting": "Andmebaasi puhastuse tegumid", + "nightly_tasks_database_cleanup_setting_description": "Eemalda andmebaasist vanad, aegunud andmed", + "nightly_tasks_generate_memories_setting": "Genereeri mälestused", + "nightly_tasks_generate_memories_setting_description": "Loo üksustest uued mälestused", + "nightly_tasks_missing_thumbnails_setting": "Genereeri puuduvad pisipildid", + "nightly_tasks_missing_thumbnails_setting_description": "Suuna ilma pisipiltideta üksused pisipiltide genereerimisele", + "nightly_tasks_settings": "Öiste tegumite seaded", + "nightly_tasks_settings_description": "Halda öiseid tegumeid", + "nightly_tasks_start_time_setting": "Algusaeg", + "nightly_tasks_start_time_setting_description": "Aeg, millal server alustab öiste tegumite käivitamist", + "nightly_tasks_sync_quota_usage_setting": "Sünkrooni kvoodikasutus", + "nightly_tasks_sync_quota_usage_setting_description": "Uuenda kasutaja talletuskvoot jooksva kasutuse alusel", "no_paths_added": "Ühtegi teed pole", "no_pattern_added": "Mustreid ei ole", "note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobiilne ümbersuunamise URI", "oauth_mobile_redirect_uri_override": "Mobiilse ümbersuunamise URI ülekirjutamine", "oauth_mobile_redirect_uri_override_description": "Lülita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks ''{callback}''", + "oauth_role_claim": "Rolli väide", + "oauth_role_claim_description": "Anna selle väite olemasolul automaatselt administraatori ligipääs. Väite väärtus võib olla 'user' või 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", @@ -357,10 +373,12 @@ "admin_password": "Administraatori parool", "administration": "Administratsioon", "advanced": "Täpsemad valikud", + "advanced_settings_beta_timeline_subtitle": "Koge uut rakendust", + "advanced_settings_beta_timeline_title": "Beeta ajajoon", "advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sünkroonimise ajal üksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kõigi albumite tuvastamisega.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sünkroonimise filtrit", "advanced_settings_log_level_title": "Logimistase: {level}", - "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad seadmes olevate üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", + "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad lokaalsete üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", "advanced_settings_prefer_remote_title": "Eelista kaugpilte", "advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma", "advanced_settings_proxy_headers_title": "Vaheserveri päised", @@ -388,6 +406,7 @@ "album_options": "Albumi valikud", "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", + "album_search_not_found": "Otsingule vastavaid albumeid ei leitud", "album_share_no_users": "Paistab, et oled seda albumit kõikide kasutajatega jaganud, või pole ühtegi kasutajat, kellega jagada.", "album_updated": "Album muudetud", "album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi üksuseid", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Vaikimisi albumi järjestus", "albums_default_sort_order_description": "Uute albumite lisamisel üksuste esialgne järjekord.", "albums_feature_description": "Üksuste kollektsioonid, mida saab teiste kasutajatega jagada.", + "albums_on_device_count": "Albumid seadmel ({count})", "all": "Kõik", "all_albums": "Kõik albumid", "all_people": "Kõik isikud", @@ -427,6 +447,7 @@ "app_settings": "Rakenduse seaded", "appears_in": "Albumid", "archive": "Arhiiv", + "archive_action_prompt": "{count} lisatud arhiivi", "archive_or_unarchive_photo": "Arhiveeri või taasta foto", "archive_page_no_archived_assets": "Arhiveeritud üksuseid ei leitud", "archive_page_title": "Arhiveeri ({count})", @@ -464,7 +485,6 @@ "assets": "Üksused", "assets_added_count": "{count, plural, one {# üksus} other {# üksust}} lisatud", "assets_added_to_album_count": "{count, plural, one {# üksus} other {# üksust}} albumisse lisatud", - "assets_added_to_name_count": "{count, plural, one {# üksus} other {# üksust}} lisatud {hasName, select, true {albumisse {name}} other {uude albumisse}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada", "assets_count": "{count, plural, one {# üksus} other {# üksust}}", "assets_deleted_permanently": "{count} üksus(t) jäädavalt kustutatud", @@ -587,6 +607,7 @@ "cancel": "Katkesta", "cancel_search": "Katkesta otsing", "canceled": "Tühistatud", + "canceling": "Tühistamine", "cannot_merge_people": "Ei saa isikuid ühendada", "cannot_undo_this_action": "Sa ei saa seda tagasi võtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaõnnestus", @@ -703,7 +724,7 @@ "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", - "darkTheme": "Lülita tume teema", + "dark_theme": "Lülita tume teema", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", @@ -719,6 +740,7 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", + "delete_action_prompt": "{count} jäädavalt kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?", "delete_dialog_alert": "Need üksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -732,6 +754,7 @@ "delete_key": "Kustuta võti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_action_prompt": "{count} kustutatud lokaalselt", "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", @@ -745,6 +768,7 @@ "description": "Kirjeldus", "description_input_hint_text": "Lisa kirjeldus...", "description_input_submit_error": "Viga kirjelduse muutmisel, rohkem infot leiad logist", + "deselect_all": "Eemalda kõik valikust", "details": "Üksikasjad", "direction": "Suund", "disabled": "Välja lülitatud", @@ -762,6 +786,7 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_action_prompt": "{count} üksust laaditakse alla", "download_canceled": "Allalaadimine katkestatud", "download_complete": "Allalaadimine lõpetatud", "download_enqueue": "Allalaadimine ootel", @@ -799,6 +824,7 @@ "edit_key": "Muuda võtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_action_prompt": "{count} asukoht muudetud", "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", @@ -817,6 +843,7 @@ "empty_trash": "Tühjenda prügikast", "empty_trash_confirmation": "Kas oled kindel, et soovid prügikasti tühjendada? See eemaldab kõik seal olevad üksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi võtta!", "enable": "Luba", + "enable_backup": "Luba varundus", "enable_biometric_auth_description": "Biomeetrilise autentimise lubamiseks sisesta oma PIN-kood", "enabled": "Lubatud", "end_date": "Lõppkuupäev", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Üksuste laadimine ebaõnnestus", "failed_to_load_folder": "Kausta laadimine ebaõnnestus", "favorite": "Lemmik", + "favorite_action_prompt": "{count} lisatud lemmikutesse", "favorite_or_unfavorite_photo": "Lisa foto lemmikutesse või eemalda lemmikutest", "favorites": "Lemmikud", "favorites_page_no_favorites": "Lemmikuid üksuseid ei leitud", @@ -1099,7 +1127,7 @@ "ios_debug_info_no_processes_queued": "Taustaprotsesse pole järjekorras", "ios_debug_info_no_sync_yet": "Taustal sünkroonimise tööde pole veel käinud", "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprotsess järjekorras} other {{count} taustaprotsessi järjekorras}}", - "ios_debug_info_processing_ran_at": "Töötlemine käis {dateTime}", + "ios_debug_info_processing_ran_at": "Töötlemine toimus {dateTime}", "items_count": "{count, plural, one {# üksus} other {# üksust}}", "jobs": "Tööted", "keep": "Jäta alles", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Loomise aeg", "library_page_sort_last_modified": "Viimase muutmise aeg", "library_page_sort_title": "Albumi pealkiri", + "licenses": "Litsentsid", "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", @@ -1246,6 +1275,7 @@ "more": "Rohkem", "move": "Liiguta", "move_off_locked_folder": "Liiguta lukustatud kaustast välja", + "move_to_lock_folder_action_prompt": "{count} lisatud lukustatud kausta", "move_to_locked_folder": "Liiguta lukustatud kausta", "move_to_locked_folder_confirmation": "Need fotod ja videod eemaldatakse kõigist albumitest ning nad on nähtavad ainult lukustatud kaustas", "moved_to_archive": "{count, plural, one {# üksus} other {# üksust}} liigutatud arhiivi", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Toetaja staatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevõtit haldab administraator", + "queue_status": "Järjekorras {count}/{total}", "rating": "Hinnang", "rating_clear": "Tühjenda hinnang", "rating_count": "{count, plural, one {# tärn} other {# tärni}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Eemalda kohandatud kuupäevavahemik", "remove_deleted_assets": "Eemalda kustutatud üksused", "remove_from_album": "Eemalda albumist", + "remove_from_album_action_prompt": "{count} eemaldatud albumist", "remove_from_favorites": "Eemalda lemmikutest", + "remove_from_lock_folder_action_prompt": "{count} eemaldatud lukustatud kaustast", "remove_from_locked_folder": "Eemalda lukustatud kaustast", "remove_from_locked_folder_confirmation": "Kas oled kindel, et soovid need fotod ja videod lukustatud kaustast välja liigutada? Need muutuvad su kogus nähtavaks.", "remove_from_shared_link": "Eemalda jagatud lingist", @@ -1667,6 +1700,7 @@ "settings_saved": "Seaded salvestatud", "setup_pin_code": "Seadista PIN-kood", "share": "Jaga", + "share_action_prompt": "Jagatud {count} üksust", "share_add_photos": "Lisa fotosid", "share_assets_selected": "{count} valitud", "share_dialog_preparing": "Ettevalmistamine...", @@ -1768,6 +1802,7 @@ "sort_title": "Pealkiri", "source": "Lähtekood", "stack": "Virnasta", + "stack_action_prompt": "{count} virnastatud", "stack_duplicates": "Virnasta duplikaadid", "stack_select_one_photo": "Vali virnale kaanefoto", "stack_selected_photos": "Virnasta valitud fotod", @@ -1838,6 +1873,7 @@ "total": "Kokku", "total_usage": "Kogukasutus", "trash": "Prügikast", + "trash_action_prompt": "{count} liigutatud prügikasti", "trash_all": "Kõik prügikasti", "trash_count": "Liiguta {count, number} prügikasti", "trash_delete_asset": "Kustuta üksus", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "PIN-koodi muutmine ebaõnnestus", "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaõnnestus", "unarchive": "Taasta arhiivist", + "unarchive_action_prompt": "{count} eemaldatud arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", "undo": "Võta tagasi", "unfavorite": "Eemalda lemmikutest", + "unfavorite_action_prompt": "{count} eemaldatud lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", "unknown_country": "Tundmatu riik", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Ära vali duplikaate", "unselect_all_in": "Ära vali ühtegi grupis {group}", "unstack": "Eralda", + "unstack_action_prompt": "{count} eraldatud", "unstacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} eraldatud", + "untagged": "Sildistamata", "up_next": "Järgmine", "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi üles", "upload_concurrency": "Üleslaadimise samaaegsus", + "upload_details": "Üleslaadimise üksikasjad", "upload_dialog_info": "Kas soovid valitud üksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse üleslaadimine", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Vaata konto kasutuse statistikat", "username": "Kasutajanimi", "users": "Kasutajad", + "users_added_to_album_count": "{count, plural, one {# kasutaja} other {# kasutajat}} lisatud albumisse", "utilities": "Tööriistad", "validate": "Valideeri", "validate_endpoint_error": "Sisesta korrektne URL", @@ -1930,6 +1972,7 @@ "view_album": "Vaata albumit", "view_all": "Vaata kõiki", "view_all_users": "Vaata kõiki kasutajaid", + "view_details": "Vaata üksikasju", "view_in_timeline": "Vaata ajajoonel", "view_link": "Vaata linki", "view_links": "Vaata linke", diff --git a/i18n/fi.json b/i18n/fi.json index f41d0cf631..4e12253cf2 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Hallitse metatietoja", "migration_job": "Migraatio", "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", + "nightly_tasks_cluster_faces_setting_description": "Aja kasvojen tunnistus uusiin tunnistettuihin kasvoihin", + "nightly_tasks_cluster_new_faces_setting": "Kokoa uudet kasvot", + "nightly_tasks_database_cleanup_setting": "Tietokannan puhdistuksen tehtävät", + "nightly_tasks_database_cleanup_setting_description": "Siivoa vanhentunut data tietokannasta", + "nightly_tasks_generate_memories_setting": "Luo muistoja", + "nightly_tasks_generate_memories_setting_description": "Luo kohteista uusia muistoja", + "nightly_tasks_missing_thumbnails_setting": "Luo puuttuvat pikkukuvat", + "nightly_tasks_missing_thumbnails_setting_description": "Laita ilman pikkukuvia olevat kohteet jonoon pikkukuvien luontia varten", + "nightly_tasks_settings": "Yöllisten tehtävien asetukset", + "nightly_tasks_settings_description": "Hallitse yöllisiä tehtäviä", + "nightly_tasks_start_time_setting": "Aloitusaika", + "nightly_tasks_start_time_setting_description": "Aika jolloin palvelin aloittaa yöllisten tehtävien ajon", + "nightly_tasks_sync_quota_usage_setting": "Synkronointikiintiön käyttö", + "nightly_tasks_sync_quota_usage_setting_description": "Päivitä käyttäjän tallennustilan kiintiö nykyisen käytön mukaan", "no_paths_added": "Polkuja ei asetettu", "no_pattern_added": "Kaavoja ei lisättynä", "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override_description": "Ota käyttöön kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", + "oauth_role_claim": "Roolin vaatimus", + "oauth_role_claim_description": "Salli pääkäyttäjän pääsyoikeus automaattisesti tämän vaatimuksen perusteella. Vaatimus voi sisältää, joko 'käyttäjän' tai 'pääkäyttäjän'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", @@ -244,6 +260,7 @@ "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyö", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", + "storage_template_onboarding_description_v2": "Päälle kytkettynä, toiminto järjestestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -403,6 +420,9 @@ "album_with_link_access": "Anna kenen tahansa nähdä linkin kautta tämän albumin valokuvat ja henkilöt.", "albums": "Albumit", "albums_count": "{count, plural, one {{count, number} albumi} other {{count, number} albumia}}", + "albums_default_sort_order": "Albumin oletuslajittelujärjestys", + "albums_default_sort_order_description": "Kohteiden ensisijainen lajittelujärjestys uusia albumeja luotaessa.", + "albums_feature_description": "Kokoelma kohteita, jotka voidaan jakaa muille käyttäjille.", "all": "Kaikki", "all_albums": "Kaikki albumit", "all_people": "Kaikki henkilöt", @@ -423,6 +443,7 @@ "app_settings": "Sovellusasetukset", "appears_in": "Esiintyy albumeissa", "archive": "Arkisto", + "archive_action_prompt": "{count} lisätty arkistoon", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_page_no_archived_assets": "Arkistoituja kohteita ei löytynyt", "archive_page_title": "Arkisto ({count})", @@ -460,10 +481,12 @@ "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", - "assets_added_to_name_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}} {hasName, select, true {{name}} other {uuteen albumiin}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin", "assets_count": "{count, plural, one {# media} other {# mediaa}}", "assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi", "assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta", + "assets_downloaded_failed": "{count, plural, one {Ladattu # tiedosto - {error} tiedosto epäonnistui} other {ladattu # tiedostoa - {error} tiedostot epäonnistuivat}}", + "assets_downloaded_successfully": "{count, plural, one {Ladattu # tiedosto onnistuneesti} other {Ladattu # tiedostoa onnistuneesti}}", "assets_moved_to_trash_count": "Siirretty {count, plural, one {# media} other {# mediaa}} roskakoriin", "assets_permanently_deleted_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "assets_removed_count": "{count, plural, one {# media} other {# mediaa}} poistettu", @@ -478,6 +501,7 @@ "authorized_devices": "Valtuutetut laitteet", "automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla", "automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto", + "autoplay_slideshow": "Toista diaesitys automaattisesti", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", "background_location_permission": "Taustasijainnin käyttöoikeus", @@ -582,7 +606,8 @@ "cannot_merge_people": "Ihmisiä ei voitu yhdistää", "cannot_undo_this_action": "Et voi perua tätä toimintoa!", "cannot_update_the_description": "Kuvausta ei voi päivittää", - "cast": "Lähettää", + "cast": "Suoratoisto", + "cast_description": "Määritä saatavilla olevat suoratoistopalvelut", "change_date": "Vaihda päiväys", "change_description": "Muuta kuvausta", "change_display_order": "Muuta näyttöjärjestystä", @@ -641,6 +666,7 @@ "confirm_password": "Vahvista salasana", "confirm_tag_face": "Haluatko merkitä nämä kasvot nimellä {name}?", "confirm_tag_face_unnamed": "Merkitäänkö nämä kasvot?", + "connected_device": "Yhdistetty laite", "connected_to": "Yhdistetty", "contain": "Mahduta", "context": "Konteksti", @@ -693,6 +719,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tumma", + "dark_theme": "Vaihda tumma teema", "date_after": "Päivämäärän jälkeen", "date_and_time": "Päivämäärä ja aika", "date_before": "Päivä ennen", @@ -708,6 +735,7 @@ "default_locale": "Oletuskieliasetus", "default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan", "delete": "Poista", + "delete_action_prompt": "{count} poistettu pysyvästi", "delete_album": "Poista albumi", "delete_api_key_prompt": "Haluatko varmasti poistaa tämän API-avaimen?", "delete_dialog_alert": "Nämä kohteet poistetaan pysyvästi Immich:stä ja laitteeltasi", @@ -740,12 +768,13 @@ "disallow_edits": "Älä salli muokkauksia", "discord": "Discord", "discover": "Tutki", + "discovered_devices": "Löydetyt laitteet", "dismiss_all_errors": "Sivuuta kaikki virheet", "dismiss_error": "Sivuuta virhe", "display_options": "Näyttöasetukset", "display_order": "Näyttöjärjestys", "display_original_photos": "Näytä alkuperäiset kuvat", - "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva peukalokuvan sijasta kun alkuperäinen aineisto on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", + "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva esikatselukuvan sijasta, kun alkuperäinen kuva on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", "do_not_show_again": "Älä näytä tätä enää", "documentation": "Dokumentaatio", "done": "Valmis", @@ -787,6 +816,7 @@ "edit_key": "Muokkaa avainta", "edit_link": "Muokkaa linkkiä", "edit_location": "Muokkaa sijaintia", + "edit_location_action_prompt": "{count} sijaintia muokattu", "edit_location_dialog_title": "Sijainti", "edit_name": "Muokkaa nimeä", "edit_people": "Muokkaa henkilöitä", @@ -972,6 +1002,7 @@ "failed_to_load_assets": "Kohteiden lataus epäonnistui", "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", + "favorite_action_prompt": "{count} lisätty suosikkeihin", "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", "favorites": "Suosikit", "favorites_page_no_favorites": "Suosikkikohteita ei löytynyt", @@ -1086,6 +1117,8 @@ "ios_debug_info_last_sync_at": "Viimeisin synkronisointi {dateTime}", "ios_debug_info_no_processes_queued": "Ei taustaprosesseja jonossa", "ios_debug_info_no_sync_yet": "Taustasynkronisointia ei ole suoritettu vielä", + "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprosessi jonossa} other {{count} taustaprosessia jonossa}}", + "ios_debug_info_processing_ran_at": "Prosessi valmistui {dateTime}", "items_count": "{count, plural, one {# kpl} other {# kpl}}", "jobs": "Taustatehtävät", "keep": "Säilytä", @@ -1094,6 +1127,9 @@ "kept_this_deleted_others": "Tämä kohde säilytettiin. {count, plural, one {# asset} other {# assets}} poistettiin", "keyboard_shortcuts": "Pikanäppäimet", "language": "Kieli", + "language_no_results_subtitle": "Yritä säätää hakuehtoja", + "language_no_results_title": "Kieliä ei löydetty", + "language_search_hint": "Etsi kieliä...", "language_setting_description": "Valitse suosimasi kieli", "last_seen": "Viimeksi nähty", "latest_version": "Viimeisin versio", @@ -1119,6 +1155,7 @@ "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", + "local_asset_cast_failed": "Kohdetta, joka ei ole ladattuna palvelimelle, ei voida striimata", "local_network": "Lähiverkko", "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", "location_permission": "Sijainnin käyttöoikeus", @@ -1132,6 +1169,7 @@ "locked_folder": "Lukittu kansio", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", + "logged_in_as": "Kirjautunut käyttäjänä {user}", "logged_out_all_devices": "Kaikki laitteet kirjattu ulos", "logged_out_device": "Laite kirjattu ulos", "login": "Kirjaudu", @@ -1220,13 +1258,14 @@ "merged_people_count": "{count, plural, one {# Henkilö} other {# henkilöä}} yhdistetty", "minimize": "PIenennä", "minute": "Minuutti", - "missing": "Puuttuu", + "missing": "Puuttuvat", "model": "Malli", "month": "Kuukauden mukaan", "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", "move": "Siirrä", "move_off_locked_folder": "Siirrä pois lukitusta kansiosta", + "move_to_lock_folder_action_prompt": "{count} lisätty lukittuun kansioon", "move_to_locked_folder": "Siirrä lukittuun kansioon", "move_to_locked_folder_confirmation": "Nämä kuvat ja videot poistetaan kaikista albumeista, ja ne ovat nähtävissä vain lukitussa kansiossa", "moved_to_archive": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} arkistoon", @@ -1259,6 +1298,7 @@ "no_archived_assets_message": "Arkistoi kuvia ja videoita piilottaaksesi ne kuvat näkymästä", "no_assets_message": "NAPAUTA LATAAKSESI ENSIMMÄISEN KUVASI", "no_assets_to_show": "Ei näytettäviä kohteita", + "no_cast_devices_found": "Cast-laitteita ei löytynyt", "no_duplicates_found": "Kaksoiskappaleita ei löytynyt.", "no_exif_info_available": "EXIF-tietoa ei saatavilla", "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", @@ -1291,8 +1331,11 @@ "oldest_first": "Vanhin ensin", "on_this_device": "Laitteella", "onboarding": "Käyttöönotto", - "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin, ja ne voidaan poistaa käytöstä milloin tahansa hallinta asetuksista.", + "onboarding_locale_description": "Valitse haluamasi kieli. Voit muuttaa kieliasetuksia myöhemmin asetuksista.", + "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin ja ne voidaan milloin tahansa poistaa käytöstä asetuksista.", + "onboarding_server_welcome_description": "Määritellään seuraavaksi järjestelmäsi muutamalla yleisellä asetuksella.", "onboarding_theme_description": "Valitse väriteema istunnollesi. Voit muuttaa tämän myöhemmin asetuksistasi.", + "onboarding_user_welcome_description": "Aloitetaan!", "onboarding_welcome_user": "Tervetuloa {user}", "online": "Online", "only_favorites": "Vain suosikit", @@ -1392,7 +1435,7 @@ "previous_or_next_photo": "Kuva seuraava/edellinen", "previous_or_next_year": "Vuosi seuraava/edellinen", "primary": "Ensisijainen", - "privacy": "Yksityisyys", + "privacy": "Tietosuoja", "profile": "Profiili", "profile_drawer_app_logs": "Lokit", "profile_drawer_client_out_of_date_major": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", @@ -1472,12 +1515,15 @@ "remove_custom_date_range": "Poista aikaväliltä", "remove_deleted_assets": "Poista Offline-tiedostot", "remove_from_album": "Poista albumista", + "remove_from_album_action_prompt": "{count} poistettu albumista", "remove_from_favorites": "Poista suosikeista", + "remove_from_lock_folder_action_prompt": "{count} poistettu lukitusta albumista", "remove_from_locked_folder": "Poista lukitusta kansiosta", "remove_from_locked_folder_confirmation": "Haluatko varmasti siirtää nämä kuvat ja videot pois lukitusta kansiosta? Ne näkyvät sen jälkeen kirjastossasi.", "remove_from_shared_link": "Poista jakolinkistä", "remove_memory": "Tyhjennä muisti", "remove_photo_from_memory": "Poista kuva muistista", + "remove_tag": "Poista tunniste", "remove_url": "Poista URL", "remove_user": "Poista käyttäjä", "removed_api_key": "API-avain {name} poistettu", @@ -1584,6 +1630,7 @@ "select_album_cover": "Valitse albmin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", + "select_all_in": "Valitse kaikki {group}", "select_avatar_color": "Valitse avatarin väri", "select_face": "Valitse kasvo", "select_featured_photo": "Valitse esittelykuva", @@ -1604,6 +1651,7 @@ "server_info_box_server_url": "Palvelimen URL-osoite", "server_offline": "Palvelin Offline-tilassa", "server_online": "Palvelin Online-tilassa", + "server_privacy": "Palvelimen tietosuoja", "server_stats": "Palvelimen tilastot", "server_version": "Palvelimen versio", "set": "Aseta", @@ -1613,6 +1661,7 @@ "set_date_of_birth": "Aseta syntymäaika", "set_profile_picture": "Aseta profiilikuva", "set_slideshow_to_fullscreen": "Näytä diaesitys koko ruudulla", + "set_stack_primary_asset": "Aseta pääkohteeksi", "setting_image_viewer_help": "Kuvaa katseltaessa ensin ladataan pikkukuva, sitten keskilaatuinen pikkukuva (jos käytössä) ja lopuksi alkuperäinen (jos käytössä).", "setting_image_viewer_original_subtitle": "Ota käyttöön ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytöstä vähentääksesi datan käyttöä (sekä verkossa että laitteen välimuistissa).", "setting_image_viewer_original_title": "Lataa alkuperäinen kuva", @@ -1750,6 +1799,7 @@ "start_date": "Alkupäivä", "state": "Maakunta", "status": "Tila", + "stop_casting": "Lopeta suoratoisto", "stop_motion_photo": "Pysäytä liikkuva kuva", "stop_photo_sharing": "Lopetetaanko kuvien jakaminen?", "stop_photo_sharing_description": "{partner} ei enää pääse kuviisi.", @@ -1769,7 +1819,7 @@ "sync_albums": "Synkronoi albumit", "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", - "tag": "Lisää tunniste", + "tag": "Tunniste", "tag_assets": "Lisää tunnisteita", "tag_created": "Luotu tunniste: {tag}", "tag_feature_description": "Selaa valokuvia ja videoita, jotka on ryhmitelty loogisten tunnisteotsikoiden mukaan", @@ -1810,6 +1860,7 @@ "total": "Yhteensä", "total_usage": "Käyttö yhteensä", "trash": "Roskakori", + "trash_action_prompt": "{count} siirretty roskakoriin", "trash_all": "Vie kaikki roskakoriin", "trash_count": "Roskakori {count, number}", "trash_delete_asset": "Poista / vie roskakoriin", @@ -1827,8 +1878,11 @@ "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", "unarchive": "Palauta arkistosta", + "unarchive_action_prompt": "{count} poistettu arkistosta", "unarchived_count": "{count, plural, other {# poistettu arkistosta}}", + "undo": "Kumoa", "unfavorite": "Poista suosikeista", + "unfavorite_action_prompt": "{count} poistettu suosikeista", "unhide_person": "Poista henkilö piilosta", "unknown": "Tuntematon", "unknown_country": "Tuntematon maa", @@ -1844,8 +1898,10 @@ "unsaved_change": "Tallentamaton muutos", "unselect_all": "Poista valinnat", "unselect_all_duplicates": "Poista kaikkien kaksoiskappaleiden valinta", + "unselect_all_in": "Poista kaikki valinnat {group}", "unstack": "Pura pino", "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", + "untagged": "Ilman tunnistetta", "up_next": "Seuraavaksi", "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", @@ -1861,7 +1917,7 @@ "upload_status_uploaded": "Ladattu", "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", "upload_to_immich": "Lähetä Immichiin ({count})", - "uploading": "Lähettään", + "uploading": "Lähettää", "url": "URL", "usage": "Käyttö", "use_biometric": "Käytä biometriikkaa", @@ -1873,6 +1929,7 @@ "user_liked": "{user} tykkäsi {type, select, photo {kuvasta} video {videosta} asset {mediasta} other {tästä}}", "user_pin_code_settings": "PIN-koodi", "user_pin_code_settings_description": "Hallinnoi PIN-koodiasi", + "user_privacy": "Käyttäjän tietosuoja", "user_purchase_settings": "Osta", "user_purchase_settings_description": "Hallitse ostostasi", "user_role_set": "Tee käyttäjästä {user} {role}", diff --git a/i18n/fil.json b/i18n/fil.json index 12e74f7bad..12e6086064 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -14,10 +14,13 @@ "add_a_location": "Dagdagan ng lugar", "add_a_name": "Dagdagan ng pangalan", "add_a_title": "Dagdagan ng pamagat", + "add_endpoint": "Dagdagan ng dulo", "add_location": "Magdagdag ng lugar", "add_more_users": "Magdagdag ng mga user", "add_partner": "Magdagdag ng kasangga", + "add_path": "Magdagdag ng path", "add_photos": "Magdagdag ng litrato", + "add_tag": "Magdagdag ng tag", "add_to": "Idagdag sa…", "add_to_album": "Idagdag sa album", "add_to_album_bottom_sheet_added": "Naidagdag sa {album}", diff --git a/i18n/fr.json b/i18n/fr.json index bf57469944..cebd4b694f 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -17,7 +17,7 @@ "add_endpoint": "Ajouter une adresse", "add_exclusion_pattern": "Ajouter un schéma d'exclusion", "add_import_path": "Ajouter un chemin à importer", - "add_location": "Ajouter un lieu", + "add_location": "Ajouter une localisation", "add_more_users": "Ajouter plus d'utilisateurs", "add_partner": "Ajouter un partenaire", "add_path": "Ajouter un chemin", @@ -166,6 +166,20 @@ "metadata_settings_description": "Gestion des paramètres de métadonnées", "migration_job": "Migration", "migration_job_description": "Migration des miniatures pour les médias et les visages vers la dernière structure de dossiers", + "nightly_tasks_cluster_faces_setting_description": "Démarrer la reconnaissance faciale sur les visages nouvellement détectés", + "nightly_tasks_cluster_new_faces_setting": "Regrouper les nouveaux visages", + "nightly_tasks_database_cleanup_setting": "Tâches de nettoyage de la base de données", + "nightly_tasks_database_cleanup_setting_description": "Nettoyage ancien, données de la base de données expirées", + "nightly_tasks_generate_memories_setting": "Générer les souvenirs", + "nightly_tasks_generate_memories_setting_description": "Créer des souvenirs à partir des éléments", + "nightly_tasks_missing_thumbnails_setting": "Générer les miniatures manquantes", + "nightly_tasks_missing_thumbnails_setting_description": "Mettre en file d'attente les éléments sans miniature pour la création de miniature", + "nightly_tasks_settings": "Paramètres des tâches de nuit", + "nightly_tasks_settings_description": "Gérer les tâches de nuit", + "nightly_tasks_start_time_setting": "Heure de démarrage", + "nightly_tasks_start_time_setting_description": "Heure à laquelle le serveur commence à exécuter les tâches de nuit", + "nightly_tasks_sync_quota_usage_setting": "Synchroniser les quota d'usage", + "nightly_tasks_sync_quota_usage_setting_description": "Mettre à jour les quota d'usage de l'utilisateur, en se basant sur l'utilisation actuelle", "no_paths_added": "Aucun chemin n'a été ajouté", "no_pattern_added": "Aucun schéma d'exclusion n'a été ajouté", "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'étiquette de stockage à des médias précédemment envoyés, exécutez", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirection mobile", "oauth_mobile_redirect_uri_override": "Remplacer l'URI de redirection mobile", "oauth_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme ''{callback}''", + "oauth_role_claim": "Attribut de rôle", + "oauth_role_claim_description": "Donne automatiquement un accès en tant qu'admin, en se basant sur la présence de cet attribut. L'attribut peut avoir soit 'user' (utilisateur) soit 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gérer les paramètres de connexion OAuth", "oauth_settings_more_details": "Pour plus de détails sur cette fonctionnalité, consultez ce lien.", @@ -244,7 +260,7 @@ "storage_template_migration_info": "L'enregistrement des modèles va convertir toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux médias. Pour appliquer rétroactivement le modèle aux médias précédemment envoyés, exécutez la tâche {job}.", "storage_template_migration_job": "Tâche de migration du modèle de stockage", "storage_template_more_details": "Pour plus de détails sur cette fonctionnalité, reportez-vous au Modèle de stockage et à ses implications", - "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se répéter à la documentation.", + "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se référer à la documentation.", "storage_template_path_length": "Limite approximative de la longueur du chemin : {length, number}/{limit, number}", "storage_template_settings": "Modèle de stockage", "storage_template_settings_description": "Gérer la structure des dossiers et le nom des fichiers du média envoyé", @@ -357,10 +373,12 @@ "admin_password": "Mot de passe Admin", "administration": "Administration", "advanced": "Avancé", + "advanced_settings_beta_timeline_subtitle": "Essayer la nouvelle application", + "advanced_settings_beta_timeline_title": "Timeline de la béta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilisez cette option pour filtrer les média durant la synchronisation avec des critères alternatifs. N'utilisez cela que lorsque l'application n'arrive pas à détecter tout les albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPÉRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", "advanced_settings_log_level_title": "Niveau de journalisation : {level}", - "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources présentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", + "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources locales. Activez ce paramètre pour charger des images externes à la place.", "advanced_settings_prefer_remote_title": "Préférer les images externes", "advanced_settings_proxy_headers_subtitle": "Ajoutez des en-têtes personnalisés à chaque requête réseau", "advanced_settings_proxy_headers_title": "En-têtes de proxy", @@ -413,8 +431,8 @@ "all_videos": "Toutes les vidéos", "allow_dark_mode": "Autoriser le mode sombre", "allow_edits": "Autoriser les modifications", - "allow_public_user_to_download": "Permettre aux utilisateurs non connectés de télécharger", - "allow_public_user_to_upload": "Autoriser l'envoi aux utilisateurs non connectés", + "allow_public_user_to_download": "Permettre le téléchargement par des utilisateurs non connectés", + "allow_public_user_to_upload": "Permettre l'envoi par des utilisateurs non connectés", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "Clé API", @@ -427,6 +445,7 @@ "app_settings": "Paramètres de l'application", "appears_in": "Apparaît dans", "archive": "Archiver", + "archive_action_prompt": "{count} ajouté(s) à l'archive", "archive_or_unarchive_photo": "Archiver ou désarchiver une photo", "archive_page_no_archived_assets": "Aucun élément archivé n'a été trouvé", "archive_page_title": "Archiver ({count})", @@ -464,7 +483,6 @@ "assets": "Médias", "assets_added_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}}", "assets_added_to_album_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à l'album", - "assets_added_to_name_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Le média ne peut pas être ajouté} other {Les médias ne peuvent pas être ajoutés}} à l'album", "assets_count": "{count, plural, one {# média} other {# médias}}", "assets_deleted_permanently": "{count} média(s) supprimé(s) définitivement", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", - "darkTheme": "Basculer sur le thème sombre", + "dark_theme": "Activer le thème sombre", "date_after": "Date après", "date_and_time": "Date et heure", "date_before": "Date avant", @@ -719,6 +737,7 @@ "default_locale": "Région par défaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", + "delete_action_prompt": "{count} supprimé(s) définitivement", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clé API ?", "delete_dialog_alert": "Ces médias seront définitivement supprimés de Immich et de votre appareil", @@ -732,6 +751,7 @@ "delete_key": "Supprimer la clé", "delete_library": "Supprimer la bibliothèque", "delete_link": "Supprimer le lien", + "delete_local_action_prompt": "{count} supprimé(s) localement", "delete_local_dialog_ok_backed_up_only": "Suppression des données sauvegardées uniquement", "delete_local_dialog_ok_force": "Supprimer tout de même", "delete_others": "Supprimer les autres", @@ -762,6 +782,7 @@ "documentation": "Documentation", "done": "Terminé", "download": "Télécharger", + "download_action_prompt": "Téléchargement de {count} éléments", "download_canceled": "Téléchargement annulé", "download_complete": "Téléchargement terminé", "download_enqueue": "Téléchargement en attente", @@ -799,6 +820,7 @@ "edit_key": "Modifier la clé", "edit_link": "Modifier le lien", "edit_location": "Modifier la localisation", + "edit_location_action_prompt": "{count} localisation(s) mise(s) à jour", "edit_location_dialog_title": "Localisation", "edit_name": "Modifier le nom", "edit_people": "Modifier les personnes", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Échec du chargement des ressources", "failed_to_load_folder": "Échec de chargement du dossier", "favorite": "Favori", + "favorite_action_prompt": "{count} ajouté(s) aux Favoris", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", "favorites_page_no_favorites": "Aucun élément favori n'a été trouvé", @@ -1036,7 +1059,7 @@ "hide_password": "Masquer le mot de passe", "hide_person": "Masquer la personne", "hide_unnamed_people": "Cacher les personnes non nommées", - "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. Les éléments {failed} sont déjà dans l'album.", + "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. {failed} éléments sont déjà dans l'album.", "home_page_add_to_album_err_local": "Impossible d'ajouter des médias locaux aux albums, ils sont ignorés", "home_page_add_to_album_success": "{added} éléments ajoutés à l'album {album}.", "home_page_album_err_partner": "Impossible d'ajouter des médias d'un partenaire à un album, ils sont ignorés", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Créations les plus récentes", "library_page_sort_last_modified": "Dernière modification", "library_page_sort_title": "Titre de l'album", + "licenses": "Licences", "light": "Clair", "like_deleted": "Réaction « j'aime » supprimée", "link_motion_video": "Lier la photo animée", @@ -1246,6 +1270,7 @@ "more": "Plus", "move": "Déplacer", "move_off_locked_folder": "Déplacer en dehors du dossier verrouillé", + "move_to_lock_folder_action_prompt": "{count} ajouté(s) au dossier verrouillé", "move_to_locked_folder": "Déplacer dans le dossier verrouillé", "move_to_locked_folder_confirmation": "Ces photos et vidéos seront retirés de tout les albums et ne seront visibles que dans le dossier verrouillé", "moved_to_archive": "{count, plural, one {# élément déplacé} other {# éléments déplacés}} vers les archives", @@ -1390,7 +1415,7 @@ "photos_and_videos": "Photos et vidéos", "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos des années précédentes", - "pick_a_location": "Choisissez un lieu", + "pick_a_location": "Choisissez une localisation", "pin_code_changed_successfully": "Code PIN changé avec succès", "pin_code_reset_successfully": "Réinitialisation du code PIN réussie", "pin_code_setup_successfully": "Définition du code PIN réussie", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Supprimer la plage de date personnalisée", "remove_deleted_assets": "Supprimer les fichiers hors ligne", "remove_from_album": "Supprimer de l'album", + "remove_from_album_action_prompt": "{count} supprimé(s) de l'album", "remove_from_favorites": "Supprimer des favoris", + "remove_from_lock_folder_action_prompt": "{count} supprimé(s) du dossier verrouillé", "remove_from_locked_folder": "Supprimer du dossier verrouillé", "remove_from_locked_folder_confirmation": "Êtes vous sûr de vouloir déplacer ces photos et vidéos en dehors du dossier verrouillé ? Elles seront visibles dans votre galerie.", "remove_from_shared_link": "Supprimer des liens partagés", @@ -1510,7 +1537,7 @@ "removed_from_favorites_count": "{count, plural, one {# supprimé} other {# supprimés}} des favoris", "removed_memory": "Souvenir supprimé", "removed_photo_from_memory": "Photo supprimée du souvenir", - "removed_tagged_assets": "Tag supprimé de {count, plural, one {# média} other {# médias}}", + "removed_tagged_assets": "Étiquette supprimée de {count, plural, one {# média} other {# médias}}", "rename": "Renommer", "repair": "Réparer", "repair_no_results_message": "Les fichiers non importés ou absents s'afficheront ici", @@ -1566,8 +1593,8 @@ "search_filter_display_option_not_in_album": "Pas dans un album", "search_filter_display_options": "Options d'affichage", "search_filter_filename": "Recherche par nom de fichier", - "search_filter_location": "Lieu", - "search_filter_location_title": "Sélectionner un lieu", + "search_filter_location": "Localisation", + "search_filter_location_title": "Sélectionner une localisation", "search_filter_media_type": "Type de média", "search_filter_media_type_title": "Sélectionner type de média", "search_filter_people_title": "Sélectionner une personne", @@ -1667,6 +1694,7 @@ "settings_saved": "Paramètres sauvegardés", "setup_pin_code": "Définir un code PIN", "share": "Partager", + "share_action_prompt": "{count} éléments partagés", "share_add_photos": "Ajouter des photos", "share_assets_selected": "{count} sélectionné(s)", "share_dialog_preparing": "Préparation...", @@ -1768,6 +1796,7 @@ "sort_title": "Titre", "source": "Source", "stack": "Empiler", + "stack_action_prompt": "{count} groupé(s)", "stack_duplicates": "Empiler les doublons", "stack_select_one_photo": "Sélectionnez une photo principale pour la pile", "stack_selected_photos": "Empiler les photos sélectionnées", @@ -1838,6 +1867,7 @@ "total": "Total", "total_usage": "Utilisation globale", "trash": "Corbeille", + "trash_action_prompt": "{count} mis à la corbeille", "trash_all": "Tout supprimer", "trash_count": "Corbeille {count, number}", "trash_delete_asset": "Mettre à la corbeille/Supprimer un média", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Impossible de changer le code PIN", "unable_to_setup_pin_code": "Impossible de définir le code PIN", "unarchive": "Désarchiver", + "unarchive_action_prompt": "{count} supprimé(s) de l'archive", "unarchived_count": "{count, plural, one {# supprimé} other {# supprimés}} de l'archive", "undo": "Annuler", "unfavorite": "Enlever des favoris", + "unfavorite_action_prompt": "{count} supprimé(s) des favoris", "unhide_person": "Afficher la personne", "unknown": "Inconnu", "unknown_country": "Pays non connu", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "Désélectionner tous les doublons", "unselect_all_in": "Tout désélectionner dans {group}", "unstack": "Désempiler", + "unstack_action_prompt": "{count} non groupés", "unstacked_assets_count": "{count, plural, one {# média dépilé} other {# médias dépilés}}", + "untagged": "Étiquette supprimée", "up_next": "Suite", "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Voir les statistiques d'utilisation du compte", "username": "Nom d'utilisateur", "users": "Utilisateurs", + "users_added_to_album_count": "{count, plural, one {# utilisateur ajouté} other {# utilisateurs ajoutés}} à l'album", "utilities": "Utilitaires", "validate": "Valider", "validate_endpoint_error": "Merci d'entrer un lien valide", diff --git a/i18n/gl.json b/i18n/gl.json index 558cc48900..70146abb53 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -455,7 +455,6 @@ "assets": "Activos", "assets_added_count": "Engadido {count, plural, one {# activo} other {# activos}}", "assets_added_to_album_count": "Engadido {count, plural, one {# activo} other {# activos}} ao álbum", - "assets_added_to_name_count": "Engadido {count, plural, one {# activo} other {# activos}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", diff --git a/i18n/he.json b/i18n/he.json index 737c307f31..49973dbc63 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} נוספו למועדפים", "admin": { "add_exclusion_pattern_description": "הוספת דפוסי החרגה. נתמכת התאמת דפוסים באמצעות *, ** ו-?. כדי להתעלם מכל הקבצים בתיקיה כלשהי בשם \"Raw\", יש להשתמש ב \"**/Raw/**\". כדי להתעלם מכל הקבצים המסתיימים ב \"tif.\", יש להשתמש ב \"tif.*/**\". כדי להתעלם מנתיב מוחלט, יש להשתמש ב \"**/נתיב/להתעלמות\".", + "admin_user": "מנהל מערכת", "asset_offline_description": "תמונה מספרייה חיצונית זו לא נמצאת יותר בדיסק והועברה לאשפה. אם הקובץ הועבר מתוך הספרייה, נא לבדוק את ציר הזמן שלך עבור התמונה המקבילה החדש. כדי לשחזר תמונה זו, נא לוודא ש-Immich יכול לגשת אל נתיב הקובץ למטה ולסרוק מחדש את הספרייה.", "authentication_settings": "הגדרות התחברות", "authentication_settings_description": "ניהול סיסמה, OAuth, והגדרות התחברות אחרות", @@ -165,6 +166,20 @@ "metadata_settings_description": "ניהול הגדרות מטא-נתונים", "migration_job": "העברה", "migration_job_description": "העבר תמונות ממוזערות של תמונות ופנים למבנה התיקיות העדכני ביותר", + "nightly_tasks_cluster_faces_setting_description": "בצע זיהוי פנים עבור פרצופים שזוהו לאחרונה", + "nightly_tasks_cluster_new_faces_setting": "קבץ פנים חדשות", + "nightly_tasks_database_cleanup_setting": "משימות תחזוקה וניקוי של מסד הנתונים", + "nightly_tasks_database_cleanup_setting_description": "נקה נתונים ישנים שפג תוקפם ממסד הנתונים", + "nightly_tasks_generate_memories_setting": "יצירת זכרונות", + "nightly_tasks_generate_memories_setting_description": "צור זכרונות חדשים מהתמונות שלך", + "nightly_tasks_missing_thumbnails_setting": "צור תמונות ממוזערות חסרות", + "nightly_tasks_missing_thumbnails_setting_description": "הוסף לתור קבצים ללא תמונות ממוזערות ליצירה של תמונות ממוזערות", + "nightly_tasks_settings": "הגדרות של משימות ליליות", + "nightly_tasks_settings_description": "נהל משימות ליליות", + "nightly_tasks_start_time_setting": "זמן התחלה", + "nightly_tasks_start_time_setting_description": "השעה שבה השרת מתחיל להריץ את המשימות הליליות", + "nightly_tasks_sync_quota_usage_setting": "סנכרן את השימוש באחסון", + "nightly_tasks_sync_quota_usage_setting_description": "עדכן את מכסת האחסון של המשתמש בהתאם לשימוש הנוכחי", "no_paths_added": "לא נוספו נתיבים", "no_pattern_added": "לא נוספה תבנית", "note_apply_storage_label_previous_assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -203,7 +218,7 @@ "oauth_storage_quota_claim": "דרישת מכסת אחסון", "oauth_storage_quota_claim_description": "הגדר אוטומטית את מכסת האחסון של המשתמש לערך של דרישה זו.", "oauth_storage_quota_default": "מכסת אחסון ברירת מחדל (GiB)", - "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה (הזן 0 עבור מכסה בלתי מוגבלת).", + "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה.", "oauth_timeout": "הבקשה נכשלה – הזמן הקצוב הסתיים", "oauth_timeout_description": "זמן קצוב לבקשות (במילישניות)", "password_enable_description": "התחבר עם דוא\"ל וסיסמה", @@ -243,6 +258,7 @@ "storage_template_migration_info": "תבנית האחסון תמיר את כל ההרחבות לאותיות קטנות. שינויים בתבנית יחולו רק על תמונות חדשות. כדי להחיל באופן רטרואקטיבי את התבנית על תמונות שהועלו בעבר, הפעל את {job}.", "storage_template_migration_job": "משימת העברת תבנית אחסון", "storage_template_more_details": "לפרטים נוספים אודות תכונה זו, עיין בתבנית האחסון ובהשלכותיה", + "storage_template_onboarding_description_v2": "כאשר פיצ’ר זה מופעל, הקבצים יאורגנו אוטומטית לפי תבנית שהוגדרה על ידי המשתמש. למידע נוסף, עיין ב־תיעוד.", "storage_template_path_length": "מגבלת אורך נתיב משוערת: {length, number}/{limit, number}", "storage_template_settings": "תבנית אחסון", "storage_template_settings_description": "ניהול מבנה התיקיות ואת שם הקובץ של התמונה שהועלתה", @@ -425,6 +441,7 @@ "app_settings": "הגדרות יישום", "appears_in": "מופיע ב", "archive": "ארכיון", + "archive_action_prompt": "{count} נוספו לארכיון", "archive_or_unarchive_photo": "העבר תמונה לארכיון או הוצא אותה משם", "archive_page_no_archived_assets": "לא נמצאו תמונות בארכיון", "archive_page_title": "בארכיון ({count})", @@ -462,7 +479,6 @@ "assets": "תמונות", "assets_added_count": "{count, plural, one {נוספה תומנה #} other {נוספו # תמונות}}", "assets_added_to_album_count": "{count, plural, one {נוספה תמונה #} other {נוספו # תמונות}} לאלבום", - "assets_added_to_name_count": "{count, plural, one {תמונה # נוספה} other {# תמונות נוספו}} אל {hasName, select, true {{name}} other {אלבום חדש}}", "assets_cannot_be_added_to_album_count": "לא ניתן להוסיף את ה{count, plural, one {תמונה} other {תמונות}} לאלבום", "assets_count": "{count, plural, one {תמונה #} other {# תמונות}}", "assets_deleted_permanently": "{count} תמונות נמחקו לצמיתות", @@ -701,7 +717,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", - "darkTheme": "החלפה למצב חושך", + "dark_theme": "הפעל/כבה מצב כהה", "date_after": "תאריך אחרי", "date_and_time": "תאריך ושעה", "date_before": "תאריך לפני", @@ -711,12 +727,13 @@ "day": "יום", "deduplicate_all": "ביטול כל הכפילויות", "deduplication_criteria_1": "גודל תמונה בבתים", - "deduplication_criteria_2": "ספירת נתוני EXIF", + "deduplication_criteria_2": "כמות נתוני EXIF", "deduplication_info": "מידע על ביטול כפילויות", "deduplication_info_description": "כדי לבחור מראש תמונות באופן אוטומטי ולהסיר כפילויות בכמות גדולה, אנו מסתכלים על:", "default_locale": "שפת ברירת מחדל", "default_locale_description": "פורמט תאריכים ומספרים מבוסס שפת הדפדפן שלך", "delete": "מחק", + "delete_action_prompt": "{count} נמחקו לצמיתות", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו לצמיתות מהשרת ומהמכשיר שלך", @@ -797,6 +814,7 @@ "edit_key": "ערוך מפתח", "edit_link": "ערוך קישור", "edit_location": "ערוך מיקום", + "edit_location_action_prompt": "{count} מיקומים נערכו", "edit_location_dialog_title": "מיקום", "edit_name": "ערוך שם", "edit_people": "ערוך אנשים", @@ -982,6 +1000,7 @@ "failed_to_load_assets": "טעינת תמונות נכשלה", "failed_to_load_folder": "טעינת תיקיה נכשלה", "favorite": "מועדף", + "favorite_action_prompt": "{count} נוספו למועדפים", "favorite_or_unfavorite_photo": "הוסף או הסר תמונה מהמועדפים", "favorites": "מועדפים", "favorites_page_no_favorites": "לא נמצאו תמונות מועדפים", @@ -1148,6 +1167,7 @@ "locked_folder": "תיקיה נעולה", "log_out": "התנתק", "log_out_all_devices": "התנתק מכל המכשירים", + "logged_in_as": "מחובר כ {user}", "logged_out_all_devices": "מנותק מכל המכשירים", "logged_out_device": "מכשיר מנותק", "login": "כניסה", @@ -1243,6 +1263,7 @@ "more": "עוד", "move": "העבר", "move_off_locked_folder": "הוצאה מהתיקייה הנעולה", + "move_to_lock_folder_action_prompt": "{count} נוספו לתיקייה הנעולה", "move_to_locked_folder": "העבר לתיקיה הנעולה", "move_to_locked_folder_confirmation": "התמונות והסרטונים האלו יוסרו מכל האלבומים, ויהיו מוצגים רק בתיקיה הנעולה", "moved_to_archive": "{count, plural, one {הועברה תמונה # } other {# תמונות הועברו}} לארכיון", @@ -1492,7 +1513,9 @@ "remove_custom_date_range": "הסר טווח תאריכים מותאם", "remove_deleted_assets": "הסר קבצים לא מקוונים", "remove_from_album": "הסר מאלבום", + "remove_from_album_action_prompt": "{count} הוסרו מהאלבום", "remove_from_favorites": "הסר מהמועדפים", + "remove_from_lock_folder_action_prompt": "{count} הוסרו מהתיקייה הנעולה", "remove_from_locked_folder": "הסר מהתיקייה הנעולה", "remove_from_locked_folder_confirmation": "האם אתה בטוח שברצונך להעביר את התמונות והסרטונים האלה מחוץ לתיקייה הנעולה? הם יהיו מוצגים בספרייה שלך.", "remove_from_shared_link": "הסר מקישור משותף", @@ -1605,6 +1628,7 @@ "select_album_cover": "בחר עטיפת אלבום", "select_all": "בחר הכל", "select_all_duplicates": "בחר את כל הכפילויות", + "select_all_in": "בחר הכול בתוך {group}", "select_avatar_color": "בחר צבע תמונת פרופיל", "select_face": "בחר פנים", "select_featured_photo": "בחר תמונה מייצגת", @@ -1834,6 +1858,7 @@ "total": "סה\"כ", "total_usage": "שימוש כולל", "trash": "אשפה", + "trash_action_prompt": "{count} הועברו לאשפה", "trash_all": "העבר הכל לאשפה", "trash_count": "העבר לאשפה {count, number}", "trash_delete_asset": "העבר לאשפה/מחק תמונה", @@ -1851,9 +1876,11 @@ "unable_to_change_pin_code": "לא ניתן לשנות את קוד ה PIN", "unable_to_setup_pin_code": "לא ניתן להגדיר קוד PIN", "unarchive": "הוצא מארכיון", + "unarchive_action_prompt": "{count} הוסרו מהארכיון", "unarchived_count": "{count, plural, other {# הוצאו מהארכיון}}", "undo": "לבטל", "unfavorite": "לא מועדף", + "unfavorite_action_prompt": "{count} הוסרו מהמועדפים", "unhide_person": "בטל הסתרת אדם", "unknown": "לא ידוע", "unknown_country": "מדינה לא ידועה", @@ -1869,8 +1896,10 @@ "unsaved_change": "שינוי לא נשמר", "unselect_all": "בטל בחירה בהכל", "unselect_all_duplicates": "בטל בחירת כל הכפילויות", + "unselect_all_in": "בטל את הבחירה של הכל ב {group}", "unstack": "בטל ערימה", "unstacked_assets_count": "{count, plural, one {תמונה # הוסרה} other {# תמונות הוסרו}} מהערימה", + "untagged": "לא מתיוגים", "up_next": "הבא בתור", "updated_at": "עודכן", "updated_password": "סיסמה עודכנה", diff --git a/i18n/hi.json b/i18n/hi.json index abb1552154..2cb29370b5 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -22,6 +22,7 @@ "add_partner": "जोड़ीदार डालें", "add_path": "पथ डालें", "add_photos": "फ़ोटो डालें", + "add_tag": "चिह्नित करें", "add_to": "इसमें डालें…", "add_to_album": "एल्बम में डालें", "add_to_album_bottom_sheet_added": "{album} में डालें", @@ -33,6 +34,7 @@ "added_to_favorites_count": "पसंदीदा में {count, number} डाला गया", "admin": { "add_exclusion_pattern_description": "बहिष्करण पैटर्न जोड़ें. *, **, और ? का उपयोग करके ग्लोबिंग करना समर्थित है। \"Raw\" नामक किसी भी निर्देशिका की सभी फ़ाइलों को अनदेखा करने के लिए, \"**/Raw/**\" का उपयोग करें। \".tif\" से समाप्त होने वाली सभी फ़ाइलों को अनदेखा करने के लिए, \"**/*.tif\" का उपयोग करें। किसी पूर्ण पथ को अनदेखा करने के लिए, \"/path/to/ignore/**\" का उपयोग करें।", + "admin_user": "व्यवस्थापक उपयोगकर्ता", "asset_offline_description": "यह बाहरी लाइब्रेरी एसेट अब डिस्क पर मौजूद नहीं है और इसे ट्रैश में डाल दिया गया है। यदि फ़ाइल को लाइब्रेरी के भीतर कहीं ले जाया गया था, तो नई संबंधित एसेट के लिए अपनी टाइमलाइन देखें। इस एसेट को वापस पाने के लिए, कृपया सुनिश्चित करें कि नीचे दिए गए फ़ाइल पथ को इम्मिच द्वारा एक्सेस किया जा सकता है और फिर लाइब्रेरी को स्कैन करें।", "authentication_settings": "प्रमाणीकरण सेटिंग्स", "authentication_settings_description": "पासवर्ड, OAuth और अन्य प्रमाणीकरण सेटिंग्स प्रबंधित करें", diff --git a/i18n/hr.json b/i18n/hr.json index d2edfc9a8e..35e7aba7e0 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -462,7 +462,6 @@ "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_added_to_name_count": "Dodano {count, plural, one {# asset} other {# assets}} u {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", diff --git a/i18n/hu.json b/i18n/hu.json index 22ec6f22a1..c00fb37224 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -22,6 +22,7 @@ "add_partner": "Partner hozzáadása", "add_path": "Elérési útvonal megadása", "add_photos": "Fotók hozzáadása", + "add_tag": "Címke hozzáadása", "add_to": "Hozzáadás ide…", "add_to_album": "Felvétel albumba", "add_to_album_bottom_sheet_added": "Hozzáadva a(z) \"{album}\" albumhoz", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} hozzáadva a kedvencekhez", "admin": { "add_exclusion_pattern_description": "Kihagyási minták (pattern) megadása. A *, ** és ? helyettesítő karakterek engedélyezettek. Pl. a \"Raw\" könyvtárban tárolt összes fájl kihagyásához használható a \"**/Raw/**\". Minden \".tif\" fájl kihagyása az összes mappában: \"**/*.tif\". Abszolút elérési útvonal kihagyása: \"/kihagyni/kivant/mappa/**\".", + "admin_user": "Admin felhasználó", "asset_offline_description": "Ez a külső képtárban lévő elem már nem található, ezért a lomtárba került. Ha a fájl a képtáron belül lett áthelyezve, akkor ellenőrizd, hogy továbbra is látható az idővonaladon. Az elem visszaállításához győződj meg róla, hogy az alábbi mappa az Immich számára elérhető, majd újra fésüld át a képtárat.", "authentication_settings": "Hitelesítési beállítások", "authentication_settings_description": "Jelszó, OAuth és egyéb hitelesítési beállítások kezelése", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Adatbázis mentések engedélyezése", "backup_keep_last_amount": "Megőrizendő korábbi mentések száma", "backup_settings": "Adatbázis mentés beállításai", - "backup_settings_description": "Adatbázis mentés beállításainak kezelése. Megjegyzés: Ezek a feladatok nincsenek felügyelve, így nem kapsz értesítés meghiúsulás esetén.", + "backup_settings_description": "Adatbázis mentés beállításainak kezelése.", "cleared_jobs": "{job}: feladatai törölve", "config_set_by_file": "A konfigurációt jelenleg egy konfigurációs fájl állítja be", "confirm_delete_library": "Biztosan ki szeretnéd törölni a {library} képtárat?", @@ -155,7 +157,7 @@ "map_settings_description": "Térkép beállítások kezelése", "map_style_description": "Egy style.json térképtémára mutató URL cím", "memory_cleanup_job": "Memória takarítás", - "memory_generate_job": "Emlék generálálsa", + "memory_generate_job": "Emlék generálása", "metadata_extraction_job": "Metaadatok kinyerése", "metadata_extraction_job_description": "Metaadat információk (pl. GPS, arcok és felbontás) kinyerése minden elemből", "metadata_faces_import_setting": "Arc importálás engedélyezése", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", "note_cannot_be_changed_later": "FIGYELEM: ezt később nem lehet megváltoztatni!", "notification_email_from_address": "Feladó cím", - "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \"", + "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \". Figyelj hogy olyan címet adj meg ahonnan az email küldés engedélyezett.", "notification_email_host_description": "Email szerver kiszolgálója (pl. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Tanúsítvány hibák figyelmen kívül hagyása", "notification_email_ignore_certificate_errors_description": "TLS tanúsítvány érvényességi hibák figyelmen kívül hagyása (nem ajánlott)", @@ -202,7 +204,7 @@ "oauth_storage_quota_claim": "Tárhelykvóta igénylése", "oauth_storage_quota_claim_description": "A felhasználó tárhelykvótájának automatikus beállítása ennek az igényeltre.", "oauth_storage_quota_default": "Alapértelmezett tárhelykvóta (GiB)", - "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét (A korlátlan tárhelyhez 0-t adj meg).", + "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét.", "oauth_timeout": "Kérés időkorlátja", "oauth_timeout_description": "Kérések időkorlátja milliszekundumban", "password_enable_description": "Bejelentkezés emaillel és jelszóval", @@ -242,6 +244,7 @@ "storage_template_migration_info": "A sablon az összes kiterjesztést kisbetűssé alakítja át. A megváltozott sablon csak az újonnan feltöltött elemekre vonatkozik. A korábbi elemek visszamenőleges áthelyezéséhez ezt futtasd: {job}.", "storage_template_migration_job": "Tárhely Sablon Migrációja", "storage_template_more_details": "További részletekért erről a funkcióról lásd a Tárhely Sablon és annak következményeit a dokumentációban", + "storage_template_onboarding_description_v2": "A funkció engedélyezésével automatikusan, a felhasználó által definiált sablon alapján lesznek rendezve a fájlok. Több információért lásd a dokumentációt.", "storage_template_path_length": "Útvonal hozzávetőleges maximális hossza: {length, number}{limit, number}", "storage_template_settings": "Tárhely Sablon", "storage_template_settings_description": "A feltöltött elemek mappaszerkezetének és fájl elnevezésének kezelése", @@ -256,7 +259,7 @@ "template_email_update_album": "Album frissítve sablon", "template_email_welcome": "Üdvözlő email sablon", "template_settings": "Értesítés sablon", - "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez.", + "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez", "theme_custom_css_settings": "Egyedi CSS", "theme_custom_css_settings_description": "CSS Stíluslapokkal az Immich stílusa megváltoztatható.", "theme_settings": "Téma Beállítások", @@ -288,7 +291,7 @@ "transcoding_encoding_options": "Enkódolás beállítások", "transcoding_encoding_options_description": "Beállíthatod az enkódolt videók kódolási algoritmusát, felbontását, minőségét és egyéb beállításait", "transcoding_hardware_acceleration": "Hardveres Gyorsítás", - "transcoding_hardware_acceleration_description": "Kísérleti funkció. Sokkal gyorsabb, viszont azonos bitrátán is alacsonyabb minőséghez vezet", + "transcoding_hardware_acceleration_description": "Kísérleti funkció: gyorsabb transzkódolás, viszont azonos bitrátán alacsonyabb minőséghez vezethet", "transcoding_hardware_decoding": "Hardveres dekódolás", "transcoding_hardware_decoding_setting_description": "Lehetővé teszi az egész folyamat gyorsítását a pusztán kódolás gyorsítása helyett. Nem biztos, hogy minden videó esetén működik.", "transcoding_max_b_frames": "B-képkockák maximum száma", @@ -334,6 +337,7 @@ "user_delete_delay_settings_description": "Hány nappal az eltávolítás után legyen véglegesen törölve a felhasználó fiókja és tárolt elemei. A végleges törlés feladat minden éjfélkor fut le, hogy ellenőrizze, hogy van-e törlendő felhasználó. Ez a beállítás a következő futtatás során lép életbe.", "user_delete_immediately": "{user} felhasználója és összes eleme azonnal sorba állításra kerül a végleges törléshez .", "user_delete_immediately_checkbox": "Felhasználó és tárolt elemeinek sorba állítása azonnali törlésre", + "user_details": "Felhasználói adatok", "user_management": "Felhasználók Kezelése", "user_password_has_been_reset": "A felhasználó jelszava megváltoztatásra került:", "user_password_reset_description": "Juttasd el az átmeneti jelszót a felhasználóhoz és tájékoztasd, hogy a következő belépésnél azt majd meg kell változtatnia.", @@ -349,6 +353,7 @@ "video_conversion_job": "Videók Átkódolása", "video_conversion_job_description": "Videók átkódolása böngészőkkel és eszközökkel való széleskörű kompatibilitás érdekében" }, + "admin_email": "Admin e-mail", "admin_password": "Admin Jelszó", "administration": "Adminisztráció", "advanced": "Haladó", @@ -362,6 +367,7 @@ "advanced_settings_self_signed_ssl_subtitle": "Nem ellenőrzi a szerver SSL tanúsítványát. Önaláírt tanúsítvány esetén szükséges beállítás.", "advanced_settings_self_signed_ssl_title": "Önaláírt SSL tanúsítványok engedélyezése", "advanced_settings_sync_remote_deletions_subtitle": "Automatikusan törölni vagy visszaállítani egy elemet ezen az eszközön, ha az adott műveletet a weben hajtották végre", + "advanced_settings_sync_remote_deletions_title": "Távoli törlések szinkronizálása [KÍSÉRLETI FUNKCIÓ]", "advanced_settings_tile_subtitle": "Haladó felhasználói beállítások", "advanced_settings_troubleshooting_subtitle": "További funkciók engedélyezése hibaelhárítás céljából", "advanced_settings_troubleshooting_title": "Hibaelhárítás", @@ -398,6 +404,9 @@ "album_with_link_access": "A link birtokában bárki láthatja a fotókat és a személyeket ebben az albumban.", "albums": "Albumok", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Alapértelmezett album rendezés", + "albums_default_sort_order_description": "Alapértelmezett sorrendezés új albumok létrehozásánál.", + "albums_feature_description": "Másokkal megosztható elemek gyűjteménye.", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden személy", @@ -455,10 +464,11 @@ "assets": "Elemek", "assets_added_count": "{count, plural, other {# elem}} hozzáadva", "assets_added_to_album_count": "{count, plural, other {# elem}} hozzáadva az albumhoz", - "assets_added_to_name_count": "{count, plural, other {# elem}} hozzáadva {hasName, select, true {a(z) {name}} other {az új}} albumhoz", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Az elem} other {Az elemek}} nem adhatóak hozzá az albumhoz", "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem véglegesen törölve", "assets_deleted_permanently_from_server": "{count} elem véglegesen törölve az Immich szerverről", + "assets_downloaded_successfully": "{count, plural, one {# fájl sikeresen letöltve} other {# fájl sikeresen letöltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} áthelyezve a lomtárba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} véglegesen törölve", "assets_removed_count": "{count, plural, other {# elem}} eltávolítva", @@ -484,10 +494,10 @@ "backup_album_selection_page_selection_info": "Összegzés", "backup_album_selection_page_total_assets": "Összes egyedi elem", "backup_all": "Összes", - "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás...", - "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás...", + "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás…", + "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás…", "backup_background_service_current_upload_notification": "Feltöltés {filename}", - "backup_background_service_default_notification": "Új elemek ellenőrzése...", + "backup_background_service_default_notification": "Új elemek ellenőrzése…", "backup_background_service_error_title": "Hiba a mentés közben", "backup_background_service_in_progress_notification": "Elemek mentése folyamatban…", "backup_background_service_upload_failure_notification": "A feltöltés sikertelen {filename}", @@ -497,6 +507,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Beállítások megnyitása", "backup_controller_page_background_battery_info_link": "Mutasd meg hogyan", "backup_controller_page_background_battery_info_message": "A sikeres háttérben történő mentéshez kérjük, tiltsd le az Immich akkumulátor optimalizálását.\n\nMivel ezt a különféle eszközökön máshogy kell, ezért kérjük, az eszközöd gyártójától tudd meg, hogyan kell.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Akkumulátor optimalizálás", "backup_controller_page_background_charging": "Csak töltés közben", "backup_controller_page_background_configure_error": "A háttérszolgáltatás beállítása sikertelen", @@ -539,6 +550,10 @@ "backup_options_page_title": "Biztonági mentés beállításai", "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", + "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", + "biometric_locked_out": "A biometrikus azonosításból kizárva", + "biometric_no_options": "A biometrikus azonosítás nem elérhető", + "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", "blurred_background": "Homályos háttér", @@ -572,6 +587,7 @@ "cannot_undo_this_action": "Ez a művelet nem visszavonható!", "cannot_update_the_description": "A leírás megváltoztatása nem sikerült", "change_date": "Dátum változtatása", + "change_description": "Leírás megváltoztatása", "change_display_order": "Megjelenítési sorrend megváltoztatása", "change_expiration_time": "Lejárati idő megváltoztatása", "change_location": "Helyszín változtatása", @@ -598,6 +614,7 @@ "clear_all_recent_searches": "Legutóbbi keresések törlése", "clear_message": "Üzenet törlése", "clear_value": "Érték törlése", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Jelszó Megadása", "client_cert_import": "Importálás", "client_cert_import_success_msg": "Kliens tanúsítvány importálva", @@ -625,6 +642,10 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-ként megjelölni?", + "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", + "connected_device": "Kapcsolt eszköz", + "connected_to": "Kapcsolódva", "contain": "Belül", "context": "Kontextus", "continue": "Folytatás", @@ -633,6 +654,7 @@ "control_bottom_app_bar_delete_from_local": "Törlés az eszközről", "control_bottom_app_bar_edit_location": "Hely Módosítása", "control_bottom_app_bar_edit_time": "Dátum és Idő Módosítása", + "control_bottom_app_bar_share_link": "Link megosztása", "control_bottom_app_bar_share_to": "Megosztás Ide", "control_bottom_app_bar_trash_from_immich": "Lomtárba Helyez", "copied_image_to_clipboard": "Kép a vágólapra másolva.", @@ -664,6 +686,7 @@ "create_tag_description": "Új címke létrehozása. Beágyazott címkék esetén add meg a címke teljes elérési útvonalát, beleértve a perjeleket is.", "create_user": "Felhasználó létrehozása", "created": "Készült", + "created_at": "Létrehozva", "crop": "Kivágás", "curated_object_page_title": "Dolgok", "current_device": "Ez az eszköz", @@ -719,7 +742,9 @@ "direction": "Irány", "disabled": "Letiltott", "disallow_edits": "Módosítások letiltása", + "discord": "Discord", "discover": "Felfedez", + "discovered_devices": "Felfedezett eszközök", "dismiss_all_errors": "Minden hiba elvetése", "dismiss_error": "Hiba elvetése", "display_options": "Megjelenítési beállítások", @@ -744,8 +769,8 @@ "download_settings_description": "Elemek letöltésével kapcsolatos beállítások kezelése", "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", - "download_sucess_android": "Média letöltve a DCIM/Immich mappába\n", - "download_waiting_to_retry": "Várakozás", + "download_sucess_android": "Média letöltve a DCIM/Immich mappába", + "download_waiting_to_retry": "Várakozás újrapróbálkozáshoz", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -758,6 +783,8 @@ "edit_avatar": "Profilkép módosítása", "edit_date": "Dátum módosítása", "edit_date_and_time": "Dátum és idő módosítása", + "edit_description": "Leírás szerkesztése", + "edit_description_prompt": "Kérlek válassz egy új leírást:", "edit_exclusion_pattern": "Kizárási minta (pattern) módosítása", "edit_faces": "Arcok módosítása", "edit_import_path": "Importálási útvonal módosítása", @@ -777,18 +804,25 @@ "editor_close_without_save_title": "Szerkesztő bezárása?", "editor_crop_tool_h2_aspect_ratios": "Oldalarányok", "editor_crop_tool_h2_rotation": "Forgatás", + "email": "E-mail", + "email_notifications": "E-mail értesítések", + "empty_folder": "Ez a mappa üres", "empty_trash": "Lomtár ürítése", "empty_trash_confirmation": "Biztosan kiüríted a lomtárat? Ez az Immich lomtárában lévő összes elemet véglegesen törli.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", + "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítás engedélyezéséhez", "enabled": "Engedélyezve", "end_date": "Vég dátum", "enqueued": "Sorba állítva", - "enter_wifi_name": "Add meg a WiFi hálózat nevét", + "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", + "enter_your_pin_code": "Add meg a jelszavad", + "enter_your_pin_code_subtitle": "Add meg a jelszavad a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", "error_loading_image": "Hiba a kép betöltése közben", "error_saving_image": "Hiba: {error}", + "error_tag_face_bounding_box": "Hiba az arc megjelölése közben - nem elérhetőek a határoló koordináták", "error_title": "Hiba - valami félresikerült", "errors": { "cannot_navigate_next_asset": "Nem lehet a következő elemhez navigálni", @@ -816,10 +850,12 @@ "failed_to_keep_this_delete_others": "Nem sikerült megtartani ezt az elemet, és a többi elemet törölni", "failed_to_load_asset": "Elem betöltése sikertelen", "failed_to_load_assets": "Elemek betöltése sikertelen", + "failed_to_load_notifications": "Értesítések betöltése sikertelen", "failed_to_load_people": "Személyek betöltése sikertelen", "failed_to_remove_product_key": "Termékkulcs eltávolítása sikertelen", "failed_to_stack_assets": "Elemek csoportosítása sikertelen", "failed_to_unstack_assets": "Csoportosított elemek szétszedése sikertelen", + "failed_to_update_notification_status": "Értesítés státusz frissítése sikertelen", "import_path_already_exists": "Ez az importálási útvonal már létezik.", "incorrect_email_or_password": "Helytelen email vagy jelszó", "paths_validation_failed": "A(z) {paths, plural, one {# elérési útvonal} other {# elérési útvonal}} érvényesítése sikertelen", @@ -836,6 +872,7 @@ "unable_to_archive_unarchive": "Az elem {archived, select, true {archiválása} other {kivétele az archívumból}} sikertelen", "unable_to_change_album_user_role": "Az album felhasználói jogkörének megváltoztatása sikertelen", "unable_to_change_date": "Dátum megváltoztatása sikertelen", + "unable_to_change_description": "Leírás módosítása sikertelen", "unable_to_change_favorite": "Az elem kedvenc állapotának megváltoztatása sikertelen", "unable_to_change_location": "Hely megváltoztatása sikertelen", "unable_to_change_password": "Jelszó megváltoztatása sikertelen", @@ -879,6 +916,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "Jelszó visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -906,11 +944,14 @@ "unable_to_update_user": "Felhasználó módosítása sikertelen", "unable_to_upload_file": "Fájlfeltöltés sikertelen" }, + "exif": "Exif", "exif_bottom_sheet_description": "Leírás Hozzáadása...", "exif_bottom_sheet_details": "RÉSZLETEK", "exif_bottom_sheet_location": "HELY", "exif_bottom_sheet_people": "EMBEREK", "exif_bottom_sheet_person_add_person": "Elnevez", + "exif_bottom_sheet_person_age_months": "{months} hónap idős", + "exif_bottom_sheet_person_age_year_months": "1 év, {months} hónap idős", "exit_slideshow": "Kilépés a Diavetítésből", "expand_all": "Összes kinyitása", "experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt", @@ -928,10 +969,12 @@ "external": "Külső Képtár", "external_libraries": "Külső Képtárak", "external_network": "Külső hálózat", - "external_network_sheet_info": "Ha nem vagy a megadott WiFi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", + "external_network_sheet_info": "Ha nem vagy a megadott Wi-Fi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", "face_unassigned": "Nincs hozzárendelve", "failed": "Sikertelen", + "failed_to_authenticate": "Autentikáció sikertelen", "failed_to_load_assets": "Nem sikerült betölteni az elemeket", + "failed_to_load_folder": "Mappa betöltése sikertelen", "favorite": "Kedvenc", "favorite_or_unfavorite_photo": "Fotó kedvencnek jelölése vagy annak visszavonása", "favorites": "Kedvencek", @@ -945,14 +988,19 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", + "filter_places": "Helyek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", + "folder": "Mappa", + "folder_not_found": "Mappa nem található", "folders": "Mappák", "folders_feature_description": "A fájlrendszerben lévő fényképek és videók mappanézetben való böngészése", "forward": "Előre", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ez a funkció a Google-től tölti be a működéséhez szükséges külső adatokat.", "general": "Általános", "get_help": "Segítségkérés", - "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz.", + "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz", "getting_started": "Kezdő Lépések", "go_back": "Visszalépés", "go_to_folder": "Ugrás a mappához", @@ -981,9 +1029,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni. Kihagyjuk.", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, kihagyás", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, úghogy kihagyjuk.", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, kihagyás", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -991,11 +1039,14 @@ "home_page_delete_remote_err_local": "Helyi elemek vannak távoli törlésre kiválasztva, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", - "home_page_first_time_notice": "Ha most használod először az alkalmazást, akkor ahhoz, hogy megjelenjenek a fotók és a videók az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés.", + "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", + "home_page_locked_error_local": "Helyi elemek nem mozgathatóak a zárolt mappába, átugorva", + "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", "host": "Kiszolgáló", "hour": "Óra", + "id": "Azonosító", "ignore_icloud_photos": "iCloud fotók figyelmen kívül hagyása", "ignore_icloud_photos_description": "Az iCloud-ban tárolt fotók nem lesznek feltöltve az Immich szerverre", "image": "Kép", @@ -1035,6 +1086,11 @@ "invalid_date_format": "Érvénytelen dátumformátum", "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", + "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", + "ios_debug_info_no_processes_queued": "Nincs sorba állított hátterfolyamat", + "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", + "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", + "ios_debug_info_processing_ran_at": "Feldolgozás futott {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1043,6 +1099,8 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", + "language_no_results_subtitle": "Próbáld a keresésed módosítását", + "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", "latest_version": "Legfrissebb Verzió", @@ -1071,14 +1129,17 @@ "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", "location_picker_longitude_error": "Érvényes hosszúsági kört írj be", "location_picker_longitude_hint": "Ide írd a hosszúsági kört", + "lock": "Zárolás", + "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", + "logged_in_as": "{user}-ként belépve", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1093,7 +1154,7 @@ "login_form_err_invalid_url": "Érvénytelen cím", "login_form_err_leading_whitespace": "Az első karakter szóköz", "login_form_err_trailing_whitespace": "Az utolsó karakter szóköz", - "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver címét.", + "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver URL-t", "login_form_failed_get_oauth_server_disable": "OAuth bejelentkezés nem elérhető ezen a szerveren", "login_form_failed_login": "Hiba a bejelentkezés közben, ellenőrizd a szerver címét, az emailt és a jelszót", "login_form_handshake_exception": "SSL Kézfogási Hiba törént. Engedélyezd az önaláírt tanúsítvényokat a beállításokban, hogy ha önaláírt tanúsítványt használsz.", @@ -1145,6 +1206,9 @@ "map_settings_only_show_favorites": "Csak Kedvencek Mutatása", "map_settings_theme_settings": "Térkép Témája", "map_zoom_to_see_photos": "Kicsinyítsd, hogy láss fényképeket", + "mark_all_as_read": "Összes megjelölése olvasottként", + "mark_as_read": "Megjelölés olvasottként", + "marked_all_as_read": "Összes megjelölve olvasottként", "matches": "Azonosak", "media_type": "Médiatípus", "memories": "Emlékek", @@ -1169,6 +1233,12 @@ "month": "Hónap", "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", + "move": "Áthelyezés", + "move_off_locked_folder": "Zárolt mappából kivonás", + "move_to_locked_folder": "Áthelyezés a zárolt mappába", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappából lesznek elérhetőek", + "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", + "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", "multiselect_grid_edit_date_time_err_read_only": "Csak-olvasható elem(ek) dátuma nem módosítható, ezért kihagyjuk", "multiselect_grid_edit_gps_err_read_only": "Csak-olvasható elem(ek) helye nem módosítható, ezért kihagyjuk", @@ -1184,6 +1254,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót az oldal biztosítására", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1201,7 +1272,9 @@ "no_explore_results_message": "Tölts fel több képet, hogy böngészhesd a gyűjteményed.", "no_favorites_message": "Add hozzá a kedvencekhez, hogy gyorsan megtaláld a legjobb képeidet és videóidat", "no_libraries_message": "Hozz létre külső képtárat a fényképeid és videóid megtekintéséhez", + "no_locked_photos_message": "A zárolt mappában elhelyezett fotók és videók rejtettek, és nem jelennek meg a könyvtárad böngészése vagy keresése közben sem.", "no_name": "Nincs Név", + "no_notifications": "Nincsenek értesítések", "no_places": "Nincsenek helyek", "no_results": "Nincs találat", "no_results_description": "Próbálkozz szinonimákkal vagy általánosabb kulcsszavakkal", @@ -1210,6 +1283,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", + "nothing_here_yet": "Itt még nincs semmi", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1217,15 +1291,21 @@ "notification_toggle_setting_description": "Email értesítések engedélyezése", "notifications": "Értesítések", "notifications_setting_description": "Értesítések kezelése", + "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", + "offline": "Offline", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", "onboarding": "Első lépések", - "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak az adminisztrációs beállításokban.", + "onboarding_locale_description": "Válaszd ki a preferált nyelved. Ezt később a beállításokban bármikor módosíthatod.", + "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak a beállításokban.", "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", + "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", + "online": "Online", "only_favorites": "Csak kedvencek", + "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", "open_in_openstreetmap": "Megnyitás OpenStreetMap-ben", "open_the_search_filters": "Keresési szűrők megnyitása", @@ -1238,6 +1318,7 @@ "other_variables": "Egyéb változók", "owned": "Tulajdonos", "owner": "Tulajdonos", + "partner": "Partner", "partner_can_access": "{partner} hozzáférhet", "partner_can_access_assets": "Minden fényképed és videód, kivéve az Archiváltak és a Töröltek", "partner_can_access_location": "A helyszín, ahol a fotókat készítették", @@ -1247,7 +1328,7 @@ "partner_page_no_more_users": "Nincs több hozzáadható felhasználó", "partner_page_partner_add_failed": "Partner hozzáadása sikertelen", "partner_page_select_partner": "Partner kiválasztása", - "partner_page_shared_to_title": "Megosztva: ", + "partner_page_shared_to_title": "Megosztva", "partner_page_stop_sharing_content": "{partner} nem fog többé hozzáférni a fotóidhoz.", "partner_sharing": "Partner Megosztás", "partners": "Partnerek", @@ -1277,6 +1358,8 @@ "permanently_delete_assets_prompt": "Biztos, hogy véglegesen törölni {count, plural, one {szeretnéd ezt az elemet} other {szeretnél # elemet}}? Ez el fogja távolítani az {count, plural, one {elemet az albumokból, amikben szerepel} other {elemeket az albumokból, amikben szerepelnek}}.", "permanently_deleted_asset": "Elem véglegesen törölve", "permanently_deleted_assets_count": "{count, plural, other {# elem}} véglegesen törölve", + "permission": "Jogosultság", + "permission_empty": "A jogosultság nem hagyható üresen", "permission_onboarding_back": "Vissza", "permission_onboarding_continue_anyway": "Folytatás mindenképp", "permission_onboarding_get_started": "Vágjunk bele", @@ -1284,7 +1367,7 @@ "permission_onboarding_permission_denied": "Hozzáférés megtagadva. Az Immich használatához engedélyezni kell a fotó és videó hozzáférést a Beállításokban.", "permission_onboarding_permission_granted": "Hozzáférés engedélyezve! Minden készen áll.", "permission_onboarding_permission_limited": "Korlátozott hozzáférés. Ha szeretnéd, hogy az Immich a teljes galéria gyűjteményedet mentse és kezelje, akkor a Beállításokban engedélyezd a fotó és videó jogosultságokat.", - "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz", + "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz.", "person": "Személy", "person_birthdate": "Született: {date}", "person_hidden": "{name}{hidden, select, true { (rejtett)} other {}}", @@ -1304,19 +1387,25 @@ "play_memories": "Emlékek lejátszása", "play_motion_photo": "Mozgókép lejátszása", "play_or_pause_video": "Videó elindítása vagy megállítása", + "port": "Port", "preferences_settings_subtitle": "Alkalmazásbeállítások kezelése", "preferences_settings_title": "Beállítások", "preset": "Sablon", "preview": "Előnézet", "previous": "Előző", "previous_memory": "Előző emlék", - "previous_or_next_photo": "Előző vagy következő fotó", + "previous_or_next_day": "Nap előre/hátra", + "previous_or_next_month": "Hónap előre/hátra", + "previous_or_next_photo": "Fotó előre/hátra", + "previous_or_next_year": "Év előre/hátra", "primary": "Elsődleges", "privacy": "Magánszféra", + "profile": "Profil", "profile_drawer_app_logs": "Naplók", "profile_drawer_client_out_of_date_major": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_client_out_of_date_minor": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_drawer_client_server_up_to_date": "A Kliens és a Szerver is naprakész", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "A szerver elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_image_of_user": "{user} profilképe", @@ -1370,6 +1459,8 @@ "recent_searches": "Legutóbbi keresések", "recently_added": "Nemrég hozzáadott", "recently_added_page_title": "Nemrég Hozzáadott", + "recently_taken": "Nemrég készített", + "recently_taken_page_title": "Nemrég készített", "refresh": "Frissítés", "refresh_encoded_videos": "Átkódolt videók frissítése", "refresh_faces": "Arcok frissítése", @@ -1389,9 +1480,12 @@ "remove_deleted_assets": "Törölt Elemek Eltávolítása", "remove_from_album": "Eltávolítás az albumból", "remove_from_favorites": "Eltávolítás a kedvencekből", + "remove_from_locked_folder": "Eltávolítás a zárolt mappából", + "remove_from_locked_folder_confirmation": "Biztosan ki szeretnéd venni ezeket a fotókat és videókat a zárolt mappából? Láthatóak lesznek a könyvtáradban.", "remove_from_shared_link": "Eltávolítás a megosztott linkből", "remove_memory": "Emlék eltávolítása", "remove_photo_from_memory": "Kép eltávolítása az emlékből", + "remove_tag": "Címke eltávolítása", "remove_url": "URL eltávolítása", "remove_user": "Felhasználó eltávolítása", "removed_api_key": "API Kulcs eltávolítva: {name}", @@ -1485,7 +1579,7 @@ "search_result_page_new_search_hint": "Új Keresés", "search_settings": "Keresési beállítások", "search_state": "Megye/Állam keresése...", - "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz: ", + "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz ", "search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés", "search_tags": "Címkék keresése...", "search_timezone": "Időzóna keresése...", @@ -1517,6 +1611,7 @@ "server_info_box_server_url": "Szerver Címe", "server_offline": "Szerver Nem Elérhető", "server_online": "Szerver Elérhető", + "server_privacy": "Szerver biztonság", "server_stats": "Szerver Statisztikák", "server_version": "Szerver Verzió", "set": "Beállít", @@ -1526,6 +1621,7 @@ "set_date_of_birth": "Születési dátum beállítása", "set_profile_picture": "Profilkép beállítása", "set_slideshow_to_fullscreen": "Diavetítés teljes képernyőre állítása", + "set_stack_primary_asset": "Beállítás elsődleges elemként", "setting_image_viewer_help": "Az Elem Megjelenítő először a kis bélyegképet tölti be, aztán a közepes méretű előnézetet (ha elérhető), végül az eredetit (ha elérhető).", "setting_image_viewer_original_subtitle": "Engedélyezi az eredeti teljes felbontású kép betöltését (nagy!). Kikapcsolva csökkenti az adathasználatot (a neten és az eszköz gyorsítótárán is).", "setting_image_viewer_original_title": "Eredeti kép betöltése", @@ -1556,6 +1652,7 @@ "share_add_photos": "Fotók hozzáadása", "share_assets_selected": "{count} kiválasztva", "share_dialog_preparing": "Előkészítés...", + "share_link": "Link megosztása", "shared": "Megosztva", "shared_album_activities_input_disable": "Hozzászólások kikapcsolva", "shared_album_activity_remove_content": "Törölni szeretnéd ezt a tevékenységet?", @@ -1595,6 +1692,7 @@ "shared_link_expires_second": "{count} másodperc múlva lejár", "shared_link_expires_seconds": "{count} másodperc múlva lejár", "shared_link_individual_shared": "Egyénileg megosztva", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Megosztott linkek kezelése", "shared_link_options": "Megosztott link beállításai", "shared_links": "Megosztott linkek", @@ -1656,6 +1754,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", + "stacktrace": "Hibaleírás", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1666,6 +1765,7 @@ "stop_sharing_photos_with_user": "Fényképeid megosztásának megszüntetése ezzel a felhasználóval", "storage": "Tárhely", "storage_label": "Tárhely címke", + "storage_quota": "Tárhely kvóta", "storage_usage": "{used}/{available} használatban", "submit": "Beküldés", "suggestions": "Javaslatok", @@ -1693,11 +1793,11 @@ "theme_selection_description": "A böngésző beállításának megfelelően automatikusan használjon világos vagy sötét témát", "theme_setting_asset_list_storage_indicator_title": "Tárhely ikon mutatása az elemeken", "theme_setting_asset_list_tiles_per_row_title": "Elemek száma soronként ({count})", - "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez", + "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez.", "theme_setting_colorful_interface_title": "Színes felhasználói felület", "theme_setting_image_viewer_quality_subtitle": "Részletes képmegjelenítő minőségének beállítása", "theme_setting_image_viewer_quality_title": "Képmegjelenítő minősége", - "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez", + "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez.", "theme_setting_primary_color_title": "Alapértelmezett szín", "theme_setting_system_primary_color_title": "Rendszerszínek használata", "theme_setting_system_theme_switch": "Automatikus (követi a rendszer témáját)", @@ -1737,6 +1837,7 @@ "unable_to_setup_pin_code": "Sikertelen PIN kód beállítás", "unarchive": "Archívumból kivesz", "unarchived_count": "{count, plural, other {# elem kivéve az archívumból}}", + "undo": "Visszavonás", "unfavorite": "Kedvenc közül kivesz", "unhide_person": "Nem rejtett személy", "unknown": "Ismeretlen", @@ -1756,6 +1857,7 @@ "unstack": "Csoport Szétszedése", "unstacked_assets_count": "{count, plural, other {# elemből}} álló csoport szétszedve", "up_next": "Következik", + "updated_at": "Frissített", "updated_password": "Jelszó megváltoztatva", "upload": "Feltöltés", "upload_concurrency": "Párhuzamos feltöltés", @@ -1770,14 +1872,17 @@ "upload_success": "Feltöltés sikeres, frissítsd az oldalt az újonnan feltöltött elemek megtekintéséhez.", "upload_to_immich": "Feltöltés Immich-be ({count})", "uploading": "Feltöltés folyamatban", + "url": "URL", "usage": "Használat", "use_current_connection": "Jelenlegi kapcsolat használata", "use_custom_date_range": "Szabadon megadott időintervallum használata", "user": "Felhasználó", + "user_has_been_deleted": "Ez a felhasználó törlésre került.", "user_id": "Felhasználó azonosítója", "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", + "user_privacy": "Felhasználói biztonság", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", @@ -1822,12 +1927,12 @@ "week": "Hét", "welcome": "Üdvözlünk", "welcome_to_immich": "Üdvözöl az Immich", - "wifi_name": "WiFi Neve", + "wifi_name": "Wi-Fi Neve", "wrong_pin_code": "Hibás PIN kód", "year": "Év", "years_ago": "{years, plural, one {# évvel} other {# évvel}} ezelőtt", "yes": "Igen", "you_dont_have_any_shared_links": "Nincsenek megosztott linkjeid", - "your_wifi_name": "A WiFi hálózatod neve", + "your_wifi_name": "A Wi-Fi hálózatod neve", "zoom_image": "Kép Nagyítása" } diff --git a/i18n/id.json b/i18n/id.json index 4d15ef01e9..ff5e2524e2 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Ditambahkan {count, number} ke favorit", "admin": { "add_exclusion_pattern_description": "Tambahkan pola pengecualian. Glob menggunakan *, **, dan ? didukung. Untuk mengabaikan semua berkas dalam direktori apa pun bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua berkas berakhiran dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan jalur absolut, gunakan \"/jalur/untuk/diabaikan/**\".", + "admin_user": "Pengguna Admin", "asset_offline_description": "Aset pustaka eksternal ini tidak ada di diska dan telah dipindahkan ke tempat sampah. Jika berkasnya dipindah dalam pustaka, periksa lini masa Anda untuk aset baru yang cocok. Untuk memulihkan aset ini, pastikan jalur berkas di bawah dapat diakses oleh Immich dan pindai pustaka.", "authentication_settings": "Pengaturan Autentikasi", "authentication_settings_description": "Kelola kata sandi, OAuth, dan pengaturan autentikasi lainnya", @@ -44,7 +45,7 @@ "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", "backup_settings": "Pengaturan Pencadangan Basis Data", - "backup_settings_description": "Kelola pengaturan pencadangan basis data. Catatan: Tugas ini tidak dipantau dan Anda tidak akan diberi tahu jika ada kesalahan.", + "backup_settings_description": "Kelola pengaturan pencadangan basis data.", "cleared_jobs": "Tugas terselesaikan untuk: {job}", "config_set_by_file": "Konfigurasi saat ini ditetapkan oleh berkas konfigurasi", "confirm_delete_library": "Apakah Anda yakin ingin menghapus pustaka {library}?", @@ -165,12 +166,18 @@ "metadata_settings_description": "Kelola pengaturan metadata", "migration_job": "Migrasi", "migration_job_description": "Migrasikan gambar kecil untuk aset dan wajah ke struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", + "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", + "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", + "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_start_time_setting": "Waktu mulai", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \"", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", @@ -195,6 +202,7 @@ "oauth_mobile_redirect_uri": "URI pengalihan ponsel", "oauth_mobile_redirect_uri_override": "Penimpaan URI penerusan ponsel", "oauth_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti ''{callback}''", + "oauth_role_claim_description": "Secara otomatis memberikan akses admin berdasarkan keberadaan klaim ini. Klaim dapat berupa \"user\" atau \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", @@ -243,6 +251,7 @@ "storage_template_migration_info": "Templat penyimpanan akan mengubah semua ekstensi ke huruf kecil. Perubahan templat hanya akan diterapkan pada aset baru. Untuk menerapkan templat pada setiap aset yang sebelumnya telah diunggah, jalankan {job}.", "storage_template_migration_job": "Tugas Migrasi Templat Ruang Penyimpanan", "storage_template_more_details": "Untuk detail lebih lanjut tentang fitur ini, pergi ke Templat Penyimpanan dan kekurangannya", + "storage_template_onboarding_description_v2": "Saat diaktifkan, fitur ini akan mengatur file secara otomatis berdasarkan templat yang ditentukan pengguna. Untuk informasi selengkapnya, silakan lihat dokumentasi.", "storage_template_path_length": "Batas panjang jalur: {length, number}{limit, number}", "storage_template_settings": "Templat Penyimpanan", "storage_template_settings_description": "Kelola struktur folder dan nama berkas dari aset yang diunggah", @@ -257,7 +266,7 @@ "template_email_update_album": "Perbarui Templat Album", "template_email_welcome": "Templat surel selamat datang", "template_settings": "Templat Notifikasi", - "template_settings_description": "Kelola templat kustom untuk notifikasi.", + "template_settings_description": "Kelola templat kustom untuk notifikasi", "theme_custom_css_settings": "CSS Kustom", "theme_custom_css_settings_description": "CSS memungkinkan desain Immich untuk diubah.", "theme_settings": "Pengaturan Tema", @@ -355,13 +364,20 @@ "admin_password": "Kata Sandi Admin", "administration": "Administrasi", "advanced": "Tingkat lanjut", + "advanced_settings_beta_timeline_subtitle": "Coba pengalaman aplikasi baru", + "advanced_settings_beta_timeline_title": "Garis waktu Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan opsi ini untuk menyaring media saat sinkronisasi berdasarkan kriteria alternatif. Hanya coba ini dengan aplikasi mendeteksi semua album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan saringan sinkronisasi album perangkat alternatif", "advanced_settings_log_level_title": "Tingkat log: {level}", "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat gambar kecil dengan cepat. Menyalakan ini akan memuat gambar kecil dari server.", "advanced_settings_prefer_remote_title": "Prioritaskan gambar dari server", + "advanced_settings_proxy_headers_subtitle": "Tentukan header proxy yang harus dikirim Immich dengan setiap permintaan jaringan", + "advanced_settings_self_signed_ssl_subtitle": "Melewati verifikasi sertifikat SSL untuk titik akhir server. Diperlukan untuk sertifikat yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Izinkan sertifikat SSL yang ditandatangani sendiri", "advanced_settings_sync_remote_deletions_subtitle": "Hapus atau pulihkan aset pada perangkat ini secara otomatis ketika tindakan dilakukan di web", "advanced_settings_sync_remote_deletions_title": "Sinkronisasi penghapusan jarak jauh [EKSPERIMENTAL]", + "advanced_settings_tile_subtitle": "Pengaturan pengguna tingkat lanjut", + "advanced_settings_troubleshooting_subtitle": "Aktifkan fitur tambahan untuk pemecahan masalah", "age_months": "Umur {months, plural, one {# bulan} other {# bulan}}", "age_year_months": "Umur 1 tahun, {months, plural, one {# bulan} other {# bulan}}", "age_years": "{years, plural, other {Umur #}}", @@ -446,7 +462,6 @@ "assets": "Aset", "assets_added_count": "{count, plural, one {# aset} other {# aset}} ditambahkan", "assets_added_to_album_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke album", - "assets_added_to_name_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke {hasName, select, true {{name}} other {album baru}}", "assets_count": "{count, plural, one {# aset} other {# aset}}", "assets_deleted_permanently": "{count} aset dihapus secara permanen", "assets_deleted_permanently_from_server": "{count} aset dihapus secara permanen dari server Immich", diff --git a/i18n/it.json b/i18n/it.json index 07e19736a7..5e12bdfc97 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -87,7 +87,7 @@ "image_settings": "Impostazioni delle immagini", "image_settings_description": "Gestisci qualità e risoluzione delle immagini generate", "image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come la sequenza temporale principale", - "image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore più alto è migliore, ma produce file più grandi e può ridurre la reattività dell'app.", + "image_thumbnail_quality_description": "Qualità delle anteprime da 1 a 100. Un valore più alto è migliore ma produce file più grandi e può ridurre la reattività dell'app.", "image_thumbnail_title": "Impostazioni della copertina", "job_concurrency": "Concorrenza {job}", "job_created": "Processo creato", @@ -105,7 +105,7 @@ "library_scanning_enable_description": "Attiva la scansione periodica della libreria", "library_settings": "Libreria Esterna", "library_settings_description": "Gestisci le impostazioni della libreria esterna", - "library_tasks_description": "Scansiona le librerie esterne per i nuovi aggiornamenti", + "library_tasks_description": "Scansiona le librerie esterne per risorse nuove o modificate", "library_watching_enable_description": "Osserva le librerie esterne per cambiamenti", "library_watching_settings": "Osserva librerie (SPERIMENTALE)", "library_watching_settings_description": "Osserva automaticamente i cambiamenti dei file", @@ -121,7 +121,7 @@ "machine_learning_enabled": "Attiva machine learning", "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.", "machine_learning_facial_recognition": "Riconoscimento Facciale", - "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa facce nelle immagini", + "machine_learning_facial_recognition_description": "Rileva, riconosci e raggruppa volti nelle immagini", "machine_learning_facial_recognition_model": "Modello di riconoscimento facciale", "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli più grandi sono più lenti e utilizzano più memoria, però producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", "machine_learning_facial_recognition_setting": "Attiva riconoscimento facciale", @@ -156,16 +156,30 @@ "map_settings": "Impostazioni Mappa e Posizione", "map_settings_description": "Gestisci impostazioni mappa", "map_style_description": "URL per un tema della mappa style.json", - "memory_cleanup_job": "pulizia memoria", - "memory_generate_job": "Generazione della memoria", + "memory_cleanup_job": "Pulizia dei vecchi Ricordi", + "memory_generate_job": "Generazione dei Ricordi", "metadata_extraction_job": "Estrazione Metadata", - "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascun asset, ad esempio coordinate GPS, volti e risoluzione", + "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascuna risorsa, come coordinate GPS, volti e risoluzione", "metadata_faces_import_setting": "Abilita l'importazione dei volti", "metadata_faces_import_setting_description": "Importa i volti dai dati EXIF dell'immagine e dai file sidecar", "metadata_settings": "Impostazioni Metadati", "metadata_settings_description": "Gestisci le impostazioni dei metadati", "migration_job": "Migrazione", "migration_job_description": "Migra le anteprime per gli asset e volti alla struttura di cartelle più recente", + "nightly_tasks_cluster_faces_setting_description": "Avvia riconoscimento facciale sui volti appena rilevati", + "nightly_tasks_cluster_new_faces_setting": "Raggruppa nuovi volti", + "nightly_tasks_database_cleanup_setting": "Processi di pulizia del database", + "nightly_tasks_database_cleanup_setting_description": "Ripulisci il database da file vecchi e scaduti", + "nightly_tasks_generate_memories_setting": "Genera ricordi", + "nightly_tasks_generate_memories_setting_description": "Genera nuovi ricordi a partire dalle risorse", + "nightly_tasks_missing_thumbnails_setting": "Genera anteprime mancanti", + "nightly_tasks_missing_thumbnails_setting_description": "Metti in coda le risorse senza miniatura per la generazione delle anteprime", + "nightly_tasks_settings": "Impostazioni delle attività notturne", + "nightly_tasks_settings_description": "Gestisci attività notturne", + "nightly_tasks_start_time_setting": "Tempo di avvio", + "nightly_tasks_start_time_setting_description": "Il tempo in cui il server fa partire le attività notturne", + "nightly_tasks_sync_quota_usage_setting": "Sincronizza la quota di utilizzo", + "nightly_tasks_sync_quota_usage_setting_description": "Aggiorna la quota di spazio dell'utente in base all'utilizzo corrente", "no_paths_added": "Nessun percorso aggiunto", "no_pattern_added": "Nessun pattern aggiunto", "note_apply_storage_label_previous_assets": "Nota: Per assegnare l'etichetta storage ad asset precedentemente caricati, esegui", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI reindirizzamento mobile", "oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare", "oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''", + "oauth_role_claim": "Claim del ruolo", + "oauth_role_claim_description": "Concedi automaticamente l'accesso come amministratore in base alla presenza di questo claim. Il claim può essere 'user' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestisci impostazioni di login OAuth", "oauth_settings_more_details": "Per più dettagli riguardo a questa funzionalità, consulta la documentazione.", @@ -264,8 +280,8 @@ "theme_custom_css_settings_description": "I Cascading Style Sheets (CSS) permettono di personalizzare l'interfaccia di Immich.", "theme_settings": "Impostazioni Tema", "theme_settings_description": "Gestisci la personalizzazione dell'interfaccia web di Immich", - "thumbnail_generation_job": "Generazione Miniature", - "thumbnail_generation_job_description": "Genera miniature grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", + "thumbnail_generation_job": "Genera Anteprime", + "thumbnail_generation_job_description": "Genera anteprime grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", "transcoding_acceleration_api": "API di accelerazione", "transcoding_acceleration_api_description": "L'API che interagirà con il tuo dispositivo per accelerare la transcodifica. Questa impostazione è \"best effort\": ripiegherà sulla transcodifica software in caso di fallimento. VP9 potrebbe funzionare o meno a seconda del tuo hardware.", "transcoding_acceleration_nvenc": "NVENC (richiede GPU NVIDIA)", @@ -357,25 +373,27 @@ "admin_password": "Password Amministratore", "administration": "Amministrazione", "advanced": "Avanzate", + "advanced_settings_beta_timeline_subtitle": "Prova la nuova esperienza dell'app", + "advanced_settings_beta_timeline_title": "Timeline beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa questa opzione per filtrare i contenuti multimediali durante la sincronizzazione in base a criteri alternativi. Prova questa opzione solo se riscontri problemi con il rilevamento di tutti gli album da parte dell'app.", "advanced_settings_enable_alternate_media_filter_title": "[SPERIMENTALE] Usa un filtro alternativo per la sincronizzazione degli album del dispositivo", "advanced_settings_log_level_title": "Livello log: {level}", - "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini dal dispositivo. Attivare questa impostazione per caricare invece le immagini remote.", + "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini locali. Attivare questa impostazione per caricare invece le immagini remote.", "advanced_settings_prefer_remote_title": "Preferisci immagini remote", "advanced_settings_proxy_headers_subtitle": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", "advanced_settings_proxy_headers_title": "Header Proxy", "advanced_settings_self_signed_ssl_subtitle": "Salta la verifica dei certificati SSL del server. Richiesto con l'uso di certificati self-signed.", "advanced_settings_self_signed_ssl_title": "Consenti certificati SSL self-signed", - "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo se l'azione è stata fatta via web", + "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo quando l'azione è stata fatta via web", "advanced_settings_sync_remote_deletions_title": "Sincronizza le cancellazioni remote [SPERIMENTALE]", - "advanced_settings_tile_subtitle": "Impostazioni aggiuntive utenti", + "advanced_settings_tile_subtitle": "Impostazioni avanzate dell'utente", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", "age_months": "Età {months, plural, one {# mese} other {# mesi}}", "age_year_months": "Età 1 anno, {months, plural, one {# mese} other {# mesi}}", "age_years": "{years, plural, one {# anno} other {# anni}}", "album_added": "Album aggiunto", - "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto a un album condiviso", + "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto ad un album condiviso", "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", @@ -394,32 +412,32 @@ "album_user_left": "{album} abbandonato", "album_user_removed": "Utente {user} rimosso", "album_viewer_appbar_delete_confirm": "Sei sicuro di voler rimuovere questo album dal tuo account?", - "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album", - "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album", - "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album", - "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album", + "album_viewer_appbar_share_err_delete": "Non è stato possibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Non è stato possibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere elementi dall'album", + "album_viewer_appbar_share_err_title": "Non è stato possibile cambiare il titolo dell'album", "album_viewer_appbar_share_leave": "Lascia album", "album_viewer_appbar_share_to": "Condividi a", "album_viewer_page_share_add_users": "Aggiungi utenti", "album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", - "albums_default_sort_order": "Ordinamento album predefinito", - "albums_default_sort_order_description": "Ordine iniziale degli asset alla creazione di nuovi album.", - "albums_feature_description": "Collezione di asset che possono essere condivisi con altri utenti.", + "albums_default_sort_order": "Ordinamento predefinito degli album", + "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", + "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", "all_videos": "Tutti i video", "allow_dark_mode": "Permetti Tema Scuro", - "allow_edits": "Permetti Modifiche", + "allow_edits": "Permetti modifiche", "allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare", "allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare", "alt_text_qr_code": "Immagine QR", "anti_clockwise": "Senso anti-orario", "api_key": "Chiave API", - "api_key_description": "Il valore verrà mostrato solo una volta. Assicurati di copiarlo prima di chiudere la finestra.", - "api_key_empty": "Il nome della chiave API non può essere vuoto", + "api_key_description": "Questo valore verrà mostrato una sola volta. Assicurati di copiarlo prima di chiudere la finestra.", + "api_key_empty": "Il nome della chiave API non dovrebbe essere vuoto", "api_keys": "Chiavi API", "app_bar_signout_dialog_content": "Sei sicuro di volerti disconnettere?", "app_bar_signout_dialog_ok": "Si", @@ -427,8 +445,9 @@ "app_settings": "Impostazioni Applicazione", "appears_in": "Compare in", "archive": "Archivio", + "archive_action_prompt": "Aggiunti {count} elementi all'Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", - "archive_page_no_archived_assets": "Nessuna oggetto archiviato", + "archive_page_no_archived_assets": "Non è stato trovato nessun elemento archiviato", "archive_page_title": "Archivio ({count})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", @@ -440,42 +459,41 @@ "asset_action_share_err_offline": "Non è possibile recuperare le risorse offline, azione ignorata", "asset_added_to_album": "Aggiunto all'album", "asset_adding_to_album": "Aggiungendo all'album…", - "asset_description_updated": "La descrizione del media è stata aggiornata", + "asset_description_updated": "La descrizione dell'elemento è stata aggiornata", "asset_filename_is_offline": "Il media {filename} è offline", "asset_has_unassigned_faces": "Il media ha dei volti non categorizzati", "asset_hashing": "Hashing in corso …", "asset_list_group_by_sub_title": "Raggruppa per", "asset_list_layout_settings_dynamic_layout_title": "Layout dinamico", "asset_list_layout_settings_group_automatically": "Automatico", - "asset_list_layout_settings_group_by": "Raggruppa le risorse per", + "asset_list_layout_settings_group_by": "Raggruppa gli elementi per", "asset_list_layout_settings_group_by_month_day": "Mese + giorno", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Impostazion del layout della griglia delle foto", + "asset_list_settings_subtitle": "Impostazioni del layout della griglia delle foto", "asset_list_settings_title": "Griglia foto", - "asset_offline": "Risorsa Offline", - "asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.", - "asset_restored_successfully": "Asset ripristinato con successo", + "asset_offline": "Elemento Offline", + "asset_offline_description": "Questo elemento esterno non viene più trovato sul disco. Contatta il tuo amministratore di Immich per assistenza.", + "asset_restored_successfully": "Elemento ripristinato con successo", "asset_skipped": "Saltato", "asset_skipped_in_trash": "Nel cestino", "asset_uploaded": "Caricato", "asset_uploading": "Caricamento…", - "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore risorse", + "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore della galleria", "asset_viewer_settings_title": "Visualizzazione risorse", "assets": "Risorse", "assets_added_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}}", "assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album", - "assets_added_to_name_count": "Aggiunti {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {L'asset} other {Gli asset}} non possono essere aggiunti all'album", - "assets_count": "{count, plural, other {# asset}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {L'elemento} other {Gli elementi}} non possono essere aggiunti all'album", + "assets_count": "{count, plural, one {# elemento} other {# elementi}}", "assets_deleted_permanently": "{count} elementi cancellati definitivamente", "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", - "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riusciti} other {Scaricati # file - {error} file non riusciti}}", + "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riuscito} other {Scaricati # file - {error} file non riusciti}}", "assets_downloaded_successfully": "{count, plural, one {Scaricato # file con successo} other {Scaricati # file con successo}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", + "assets_moved_to_trash_count": "{count, plural, one {# elemento spostato} other {# elementi spostati}} nel cestino", "assets_permanently_deleted_count": "{count, plural, one {# asset cancellato} other {# asset cancellati}} definitivamente", "assets_removed_count": "{count, plural, one {# asset rimosso} other {# asset rimossi}}", "assets_removed_permanently_from_device": "{count} elementi cancellati definitivamente dal tuo dispositivo", - "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli asset cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", + "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli elementi cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", "assets_restored_count": "{count, plural, one {# asset ripristinato} other {# asset ripristinati}}", "assets_restored_successfully": "{count} elementi ripristinati", "assets_trashed": "{count} elementi cestinati", @@ -497,7 +515,7 @@ "backup_album_selection_page_selection_info": "Informazioni sulla selezione", "backup_album_selection_page_total_assets": "Numero totale delle risorse", "backup_all": "Tutti", - "backup_background_service_backup_failed_message": "Impossibile caricare i contenuti. Riprovo…", + "backup_background_service_backup_failed_message": "È stato impossibile fare il backup dei contenuti. Riprovo…", "backup_background_service_connection_failed_message": "Impossibile connettersi al server. Riprovo…", "backup_background_service_current_upload_notification": "Caricamento di {filename} in corso", "backup_background_service_default_notification": "Ricerca di nuovi contenuti…", @@ -506,7 +524,7 @@ "backup_background_service_upload_failure_notification": "Impossibile caricare {filename}", "backup_controller_page_albums": "Backup Album", "backup_controller_page_background_app_refresh_disabled_content": "Attiva l'aggiornamento dell'app in background in Impostazioni > Generale > Aggiorna app in background per utilizzare backup in background.", - "backup_controller_page_background_app_refresh_disabled_title": "Backup in background è disattivo", + "backup_controller_page_background_app_refresh_disabled_title": "Aggiornamento dell'app in background disattivo", "backup_controller_page_background_app_refresh_enable_button_text": "Vai alle impostazioni", "backup_controller_page_background_battery_info_link": "Mostrami come", "backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.", @@ -515,12 +533,12 @@ "backup_controller_page_background_charging": "Solo durante la ricarica", "backup_controller_page_background_configure_error": "Impossibile configurare i servizi in background", "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {duration}", - "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di tutti i nuovi contenuti senza la necessità di aprire l'app", - "backup_controller_page_background_is_off": "Backup automatico disattivato", - "backup_controller_page_background_is_on": "Backup automatico attivo", + "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di nuovi contenuti senza la necessità di aprire l'app", + "backup_controller_page_background_is_off": "Backup automatico in background disattivato", + "backup_controller_page_background_is_on": "Backup automatico in background attivo", "backup_controller_page_background_turn_off": "Disabilita servizi in background", "backup_controller_page_background_turn_on": "Abilita servizi in background", - "backup_controller_page_background_wifi": "Solo Wi-Fi", + "backup_controller_page_background_wifi": "Solo con Wi-Fi", "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", - "darkTheme": "Attiva/Disattiva tema scuro", + "dark_theme": "Imposta tema scuro", "date_after": "Data dopo", "date_and_time": "Data e ora", "date_before": "Data prima", @@ -719,6 +737,7 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", + "delete_action_prompt": "{count} elementi eliminati definitivamente", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -799,6 +818,7 @@ "edit_key": "Modifica chiave", "edit_link": "Modifica link", "edit_location": "Modifica posizione", + "edit_location_action_prompt": "{count} luoghi modificati", "edit_location_dialog_title": "Posizione", "edit_name": "Modifica nome", "edit_people": "Modifica persone", @@ -984,6 +1004,7 @@ "failed_to_load_assets": "Impossibile caricare gli asset", "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", + "favorite_action_prompt": "{count} elementi aggiunti ai preferiti", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", "favorites_page_no_favorites": "Nessun preferito", @@ -1127,6 +1148,7 @@ "library_page_sort_created": "Data di creazione", "library_page_sort_last_modified": "Ultima modifica", "library_page_sort_title": "Titolo album", + "licenses": "Licenze", "light": "Chiaro", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", @@ -1246,6 +1268,7 @@ "more": "Di più", "move": "Sposta", "move_off_locked_folder": "Sposta al di fuori della cartella privata", + "move_to_lock_folder_action_prompt": "{count} elementi aggiunti alla cartella sicura", "move_to_locked_folder": "Sposta nella cartella privata", "move_to_locked_folder_confirmation": "Queste foto e video verranno rimossi da tutti gli album, e saranno visibili solo dalla cartella privata", "moved_to_archive": "Spostati {count, plural, one {# asset} other {# assets}} nell'archivio", @@ -1479,13 +1502,13 @@ "recently_taken_page_title": "Scattate di Recente", "refresh": "Aggiorna", "refresh_encoded_videos": "Ricarica video codificati", - "refresh_faces": "Aggiorna facce", + "refresh_faces": "Aggiorna volti", "refresh_metadata": "Ricarica metadati", "refresh_thumbnails": "Ricarica anteprime", "refreshed": "Aggiornato", "refreshes_every_file": "Rilegge tutti i file esistenti e nuovi", "refreshing_encoded_video": "Ricaricando il video codificato", - "refreshing_faces": "Aggiorna Facce", + "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", "remove": "Rimuovi", @@ -1496,6 +1519,7 @@ "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", "remove_from_favorites": "Rimuovi dai preferiti", + "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", "remove_from_locked_folder_confirmation": "Sei sicuro di voler spostare queste foto e questi video dalla cartella privata? Diventeranno visibili nella vostra libreria.", "remove_from_shared_link": "Rimuovi dal link condiviso", @@ -1838,6 +1862,7 @@ "total": "Totale", "total_usage": "Utilizzo totale", "trash": "Cestino", + "trash_action_prompt": "{count} elementi spostati nel cestino", "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", diff --git a/i18n/ja.json b/i18n/ja.json index 1649f978d8..69c0f368fa 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -427,6 +427,7 @@ "app_settings": "アプリ設定", "appears_in": "これらに含まれます", "archive": "アーカイブ", + "archive_action_prompt": "アーカイブに{count}項目追加しました", "archive_or_unarchive_photo": "写真をアーカイブまたはアーカイブ解除", "archive_page_no_archived_assets": "アーカイブした写真またはビデオがありません", "archive_page_title": "アーカイブ ({count})", @@ -464,7 +465,6 @@ "assets": "アセット", "assets_added_count": "{count, plural, one {#個} other {#個}}のアセットを追加しました", "assets_added_to_album_count": "{count, plural, one {#個} other {#個}}のアセットをアルバムに追加しました", - "assets_added_to_name_count": "{count, plural, one {#個} other {#個}}のアセットを{hasName, select, true {{name}} other {新しいアルバム}}に追加しました", "assets_cannot_be_added_to_album_count": "{count, plural, one {アセット} other {アセット}} はアルバムに追加できません", "assets_count": "{count, plural, one {#個} other {#個}}のアセット", "assets_deleted_permanently": "{count}項目を完全に削除しました", @@ -492,7 +492,7 @@ "background_location_permission_content": "正常にWi-Fiの名前(SSID)を獲得するにはアプリが常に詳細な位置情報にアクセスできる必要があります", "backup_album_selection_page_albums_device": "デバイス上のアルバム({count})", "backup_album_selection_page_albums_tap": "タップで選択、ダブルタップで除外", - "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ (同じ写真が複数のアルバムに登録されていることがあるため)", + "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ。 (同じ写真が複数のアルバムに登録されていることがあるため)", "backup_album_selection_page_select_albums": "アルバムを選択", "backup_album_selection_page_selection_info": "選択・除外中のアルバム", "backup_album_selection_page_total_assets": "選択されたアルバムの写真と動画の数", @@ -703,7 +703,7 @@ "daily_title_text_date": "MM DD, EE", "daily_title_text_date_year": "yyyy MM DD, EE", "dark": "ダークモード", - "darkTheme": "ダークモードを切り替え", + "dark_theme": "ダークモード切り替え", "date_after": "この日以降", "date_and_time": "日付と時間", "date_before": "この日以前", @@ -719,6 +719,7 @@ "default_locale": "デフォルトのロケール", "default_locale_description": "ブラウザのロケールに基づいて日付と数値をフォーマットします", "delete": "削除", + "delete_action_prompt": "{count}項目を完全に削除しました", "delete_album": "アルバムを削除", "delete_api_key_prompt": "本当にこのAPI キーを削除しますか?", "delete_dialog_alert": "サーバーとデバイスの両方から完全に削除されます", @@ -799,6 +800,7 @@ "edit_key": "キーを編集", "edit_link": "リンクを編集する", "edit_location": "位置情報を編集", + "edit_location_action_prompt": "{count}項目の位置情報を変更しました", "edit_location_dialog_title": "位置情報", "edit_name": "名前を変更", "edit_people": "人物を編集", @@ -984,6 +986,7 @@ "failed_to_load_assets": "アセットのロードに失敗しました", "failed_to_load_folder": "フォルダーの読み込みに失敗", "favorite": "お気に入り", + "favorite_action_prompt": "{count}項目をお気に入りに追加しました", "favorite_or_unfavorite_photo": "写真をお気にいりに登録または解除", "favorites": "お気に入り", "favorites_page_no_favorites": "お気に入り登録された項目がありません", @@ -1246,6 +1249,7 @@ "more": "もっと表示", "move": "移動", "move_off_locked_folder": "鍵付きフォルダーから出す", + "move_to_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーに追加しました", "move_to_locked_folder": "鍵付きフォルダーへ移動", "move_to_locked_folder_confirmation": "これらの写真や動画はすべてのアルバムから外され、鍵付きフォルダー内でのみ閲覧可能になります", "moved_to_archive": "{count, plural, one {#} other {#}}項目をアーカイブしました", @@ -1496,6 +1500,7 @@ "remove_deleted_assets": "オフラインのアセットを削除", "remove_from_album": "アルバムから削除", "remove_from_favorites": "お気に入り解除", + "remove_from_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーから出しました", "remove_from_locked_folder": "鍵付きフォルダーから取り除く", "remove_from_locked_folder_confirmation": "選択した写真・動画を鍵付きフォルダーの外に出してよろしいですか?ライブラリに再び表示されるようになります", "remove_from_shared_link": "共有リンクから削除", @@ -1838,6 +1843,7 @@ "total": "合計", "total_usage": "総使用量", "trash": "ゴミ箱", + "trash_action_prompt": "{count}項目をゴミ箱に移動しました", "trash_all": "全て削除", "trash_count": "{count, number}枚ゴミ箱へ移動", "trash_delete_asset": "アセットをゴミ箱へ移動/削除", diff --git a/i18n/ko.json b/i18n/ko.json index 01b29e6776..1890962e5a 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -22,6 +22,7 @@ "add_partner": "파트너 추가", "add_path": "경로 추가", "add_photos": "사진 추가", + "add_tag": "태그 추가하기", "add_to": "앨범에 추가…", "add_to_album": "앨범에 추가", "add_to_album_bottom_sheet_added": "{album}에 추가되었습니다.", @@ -33,6 +34,7 @@ "added_to_favorites_count": "즐겨찾기에 {count, number}개 추가됨", "admin": { "add_exclusion_pattern_description": "규칙에 *, ** 및 ? 를 사용할 수 있습니다. 이름이 \"Raw\"인 디렉터리의 모든 파일을 제외하려면 \"**/Raw/**\"를, \".tif\"로 끝나는 모든 파일을 제외하려면 \"**/*.tif\"를 사용하고, 절대 경로의 경우 \"/path/to/ignore/**\"와 같은 방식으로 사용합니다.", + "admin_user": "관리자", "asset_offline_description": "외부 라이브러리에 포함된 이 항목을 디스크에서 더이상 찾을 수 없어 휴지통으로 이동되었습니다. 파일이 라이브러리 내에서 이동된 경우 타임라인에서 새로 연결된 항목을 확인하세요. 항목을 복원하려면 아래의 파일 경로에 Immich가 접근할 수 있는지 확인하고 라이브러리 스캔을 진행하세요.", "authentication_settings": "인증 설정", "authentication_settings_description": "비밀번호, OAuth 및 기타 인증 설정 관리", @@ -43,7 +45,7 @@ "backup_database_enable_description": "데이터베이스 덤프 활성화", "backup_keep_last_amount": "보관할 이전 덤프의 수", "backup_settings": "데이터베이스 덤프 설정", - "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다. 참고: 이 작업은 진행 및 실패 여부를 확인할 수 없습니다.", + "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다.", "cleared_jobs": "작업 중단: {job}", "config_set_by_file": "현재 구성은 설정 파일을 통해 지정되어 있습니다.", "confirm_delete_library": "{library} 라이브러리를 삭제하시겠습니까?", @@ -164,12 +166,26 @@ "metadata_settings_description": "메타데이터 설정 관리", "migration_job": "마이그레이션", "migration_job_description": "각 항목의 섬네일 및 인물의 얼굴을 최신 폴더 구조로 마이그레이션", + "nightly_tasks_cluster_faces_setting_description": "새로 감지된 얼굴에 대하여 얼굴 인식을 실행", + "nightly_tasks_cluster_new_faces_setting": "새 얼굴 묶기", + "nightly_tasks_database_cleanup_setting": "데이터베이스 정리 작업", + "nightly_tasks_database_cleanup_setting_description": "데이터베이스에서 오래되거나 만료된 데이터 정리하기", + "nightly_tasks_generate_memories_setting": "메모리 생성하기", + "nightly_tasks_generate_memories_setting_description": "항목에서 새로운 메모리 만들기", + "nightly_tasks_missing_thumbnails_setting": "누락된 섬네일 생성하기", + "nightly_tasks_missing_thumbnails_setting_description": "섬네일이 없는 항목에 대해 섬네일 생성 대기열에 추가하기", + "nightly_tasks_settings": "야간 작업 설정", + "nightly_tasks_settings_description": "야간 작업 관리", + "nightly_tasks_start_time_setting": "시작 시간", + "nightly_tasks_start_time_setting_description": "서버가 야간 작업을 시작할 시간", + "nightly_tasks_sync_quota_usage_setting": "사용량 동기화", + "nightly_tasks_sync_quota_usage_setting_description": "현재 사용량에 따라 사용자의 사용량 갱신하기", "no_paths_added": "추가된 경로 없음", "no_pattern_added": "추가된 규칙 없음", "note_apply_storage_label_previous_assets": "참고: 이전에 업로드한 항목에도 스토리지 레이블을 적용하려면 다음을 실행합니다,", "note_cannot_be_changed_later": "주의: 추후 변경할 수 없습니다!", "notification_email_from_address": "보낸 사람 이메일", - "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \"", + "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \". 이메일을 보낼 수 있도록 허가 받은 주소만을 사용하세요.", "notification_email_host_description": "이메일 서버의 호스트 (예: smtp.immich.app)", "notification_email_ignore_certificate_errors": "인증서 오류 무시", "notification_email_ignore_certificate_errors_description": "TLS 인증서 유효성 검사 오류 무시 (권장되지 않음)", @@ -194,6 +210,7 @@ "oauth_mobile_redirect_uri": "모바일 리다이렉트 URI", "oauth_mobile_redirect_uri_override": "모바일 리다이렉트 URI 재정의", "oauth_mobile_redirect_uri_override_description": "OAuth 공급자가 ''{callback}''과 같은 모바일 URI를 제공하지 않는 경우 활성화하세요.", + "oauth_role_claim": "역할 수령", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth 로그인 설정 관리", "oauth_settings_more_details": "이 기능에 대한 자세한 내용은 문서를 참조하세요.", @@ -202,7 +219,7 @@ "oauth_storage_quota_claim": "스토리지 할당량 선택", "oauth_storage_quota_claim_description": "스토리지 할당량을 사용자가 입력한 값으로 자동 설정합니다.", "oauth_storage_quota_default": "스토리지 할당량 기본값 (GiB)", - "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량 (무제한 할당량의 경우 0 입력)", + "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량", "oauth_timeout": "요청 타임아웃", "oauth_timeout_description": "요청 타임아웃 (밀리초 단위)", "password_enable_description": "이메일과 비밀번호로 로그인", @@ -242,6 +259,7 @@ "storage_template_migration_info": "스토리지 템플릿은 모든 확장자를 소문자로 변환하며, 변경 사항은 새로 업로드한 항목에만 적용됩니다. 기존에 업로드된 항목에 적용하려면 {job}을 실행하세요.", "storage_template_migration_job": "스토리지 템플릿 마이그레이션 작업", "storage_template_more_details": "이 기능에 대한 자세한 내용은 스토리지 템플릿설명을 참조하세요.", + "storage_template_onboarding_description_v2": "활성화 시, 이 기능은 사용자 지정 템플릿에 따라 파일을 자동 분류합니다. 자세한 정보는 이 문서를 확인하세요.", "storage_template_path_length": "대략적인 경로 길이 제한: {length, number}/{limit, number}", "storage_template_settings": "스토리지 템플릿", "storage_template_settings_description": "업로드된 항목의 폴더 구조 및 파일 이름 관리", @@ -288,7 +306,7 @@ "transcoding_encoding_options": "인코딩 옵션", "transcoding_encoding_options_description": "인코딩된 동영상의 코덱, 해상도, 품질 및 기타 옵션 설정", "transcoding_hardware_acceleration": "하드웨어 가속", - "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 속도가 향상되지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", + "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 트랜스코딩이 빨라지지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", "transcoding_hardware_decoding": "하드웨어 디코딩", "transcoding_hardware_decoding_setting_description": "인코딩 가속을 위해 엔드 투 엔드 가속을 사용합니다. 모든 동영상에서 작동하지 않을 수 있습니다.", "transcoding_max_b_frames": "최대 B 프레임", @@ -354,10 +372,12 @@ "admin_password": "관리자 비밀번호", "administration": "관리", "advanced": "고급", + "advanced_settings_beta_timeline_subtitle": "새로운 앱 경험 사용하기", + "advanced_settings_beta_timeline_title": "베타 타임라인", "advanced_settings_enable_alternate_media_filter_subtitle": "이 옵션을 사용하면 동기화 중 미디어를 대체 기준으로 필터링할 수 있습니다. 앱이 모든 앨범을 제대로 감지하지 못할 때만 사용하세요.", "advanced_settings_enable_alternate_media_filter_title": "대체 기기 앨범 동기화 필터 사용 (실험적)", "advanced_settings_log_level_title": "로그 레벨: {level}", - "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 기기 내의 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", + "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 로컬 항목에서 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", "advanced_settings_prefer_remote_title": "서버 이미지 선호", "advanced_settings_proxy_headers_subtitle": "네트워크 요청을 보낼 때 Immich가 사용할 프록시 헤더를 정의합니다.", "advanced_settings_proxy_headers_title": "프록시 헤더", @@ -401,6 +421,9 @@ "album_with_link_access": "링크가 있는 경우 누구나 이 앨범의 사진과 인물을 볼 수 있습니다.", "albums": "앨범", "albums_count": "앨범 {count, plural, one {{count, number}개} other {{count, number}개}}", + "albums_default_sort_order": "기본 앨범 정렬 순서", + "albums_default_sort_order_description": "새 앨범을 생성할 때 기본적으로 항목을 정렬할 순서.", + "albums_feature_description": "다른 사용자와 공유할 수 있는 항목 모음.", "all": "모두", "all_albums": "모든 앨범", "all_people": "모든 인물", @@ -421,6 +444,7 @@ "app_settings": "앱 설정", "appears_in": "다음 앨범에 포함됨", "archive": "보관함", + "archive_action_prompt": "보관함에 {count}개가 추가되었습니다.", "archive_or_unarchive_photo": "보관 처리 또는 해제", "archive_page_no_archived_assets": "보관된 항목 없음", "archive_page_title": "보관함 ({count})", @@ -458,10 +482,12 @@ "assets": "항목", "assets_added_count": "{count, plural, one {#개} other {#개}} 항목 추가됨", "assets_added_to_album_count": "앨범에 항목 {count, plural, one {#개} other {#개}} 추가됨", - "assets_added_to_name_count": "{hasName, select, true {{name}} other {새 앨범}}에 항목 {count, plural, one {#개} other {#개}} 추가됨", + "assets_cannot_be_added_to_album_count": "{count, plural, one {항목} other {항목}]이 앨범에 추가될 수 없습니다.", "assets_count": "{count, plural, one {#개} other {#개}} 항목", "assets_deleted_permanently": "{count}개 항목이 영구적으로 삭제됨", "assets_deleted_permanently_from_server": "서버에서 항목 {count}개가 영구적으로 삭제됨", + "assets_downloaded_failed": "{count, plural, one {파일 #개 다운로드 완료 - {error}개 실패} other {파일 #개 다운로드 완료 - {error}개 실패}}", + "assets_downloaded_successfully": "{count, plural, one {#개 파일 다운로드 완료} other {#개 파일 다운로드 완료}}", "assets_moved_to_trash_count": "휴지통으로 항목 {count, plural, one {#개} other {#개}} 이동됨", "assets_permanently_deleted_count": "항목 {count, plural, one {#개} other {#개}}가 영구적으로 삭제됨", "assets_removed_count": "항목 {count, plural, one {#개} other {#개}}를 제거했습니다.", @@ -476,6 +502,7 @@ "authorized_devices": "인증된 기기", "automatic_endpoint_switching_subtitle": "지정된 Wi-Fi가 사용 가능한 경우 내부망을 통해 연결하고, 그렇지 않으면 다른 연결 방식을 사용합니다.", "automatic_endpoint_switching_title": "자동 URL 전환", + "autoplay_slideshow": "슬라이드 쇼 자동 재생", "back": "뒤로", "back_close_deselect": "뒤로, 닫기, 선택 취소", "background_location_permission": "백그라운드 위치 권한", diff --git a/i18n/lt.json b/i18n/lt.json index d5e6ff20ed..14668921d2 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -14,6 +14,7 @@ "add_a_location": "Pridėti vietovę", "add_a_name": "Pridėti vardą", "add_a_title": "Pridėti pavadinimą", + "add_endpoint": "Pridėti galutinį tašką", "add_exclusion_pattern": "Pridėti išimčių šabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", @@ -32,6 +33,8 @@ "added_to_favorites": "Pridėta prie mėgstamiausių", "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausių", "admin": { + "add_exclusion_pattern_description": "Pridėti išimčių taisyklęs. Plaikomi simboliai *,**, ir ?. Ignoruoti bet kokius failus bet kuriame aplanke užvadintame \"Raw\", naudokite \"**/RAW/**\". Ignoravimui failų su plėtiniu \".tif\", naudokite \"**/*.tiff\". Aplanko kelio nustatymams, naudokite \"/aplanko/kelias/ignoruoti/**\"", + "admin_user": "Administratorius", "asset_offline_description": "Šis išorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į šiukšliadėžę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite šį elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą žemiau nurodytu adresu, ir suvykdykite bibliotekos skenavimą.", "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptažodžių, OAuth ir kitus autentifikavimo nustatymus", @@ -42,8 +45,8 @@ "backup_database_enable_description": "Įgalinti duomenų bazės išklotinės", "backup_keep_last_amount": "Išsaugomų ankstesnių duomenų bazės išklotinių skaičius", "backup_settings": "Duomenų bazės išklotinių nustatymai", - "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: Šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", - "cleared_jobs": "Išvalyti darbai: {job}", + "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", + "cleared_jobs": "Išvalytos užduotys užduočiai: {job}", "config_set_by_file": "Konfigūracija nustatyta pagal konfigūracinį failą", "confirm_delete_library": "Ar tikrai norite ištrinti {library} biblioteką?", "confirm_delete_library_assets": "Ar tikrai norite ištrinti šią biblioteką? Šis veiksmas ištrins {count, plural, one {# contained asset} other {all # contained assets}} iš Immich ir negali būti grąžintas. Failai liks diske.", @@ -51,7 +54,7 @@ "confirm_reprocess_all_faces": "Ar tikrai norite iš naujo apdoroti visus veidus? Tai taip pat ištrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iš naujo nustatyti {user} slaptažodį?", "confirm_user_pin_code_reset": "Ar tikrai norite iš naujo nustatyti {user} PIN kodą?", - "create_job": "Sukurti darbą", + "create_job": "Sukurti užduotį", "cron_expression": "Cron išraiška", "cron_expression_description": "Nustatyti skenavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos žiūrėkite Crontab Guru", "cron_expression_presets": "Išankstiniai Cron nustatymai", @@ -62,15 +65,19 @@ "face_detection": "Veidų aptikimas", "face_detection_description": "Veidų aptikimas bibliotekos elementuose naudojant mašininį mokymąsi. Vaizdo įrašų atveju naudojama tik miniatiūra. \"Atnaujinti\" iš naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir išvalo visus esamus veidų duomenis. \"Trūkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. Veidų aptikimo darbui pasibaigus, aptikti veidai patenka į veidų atpažinimo darbų eilę, kur jie priskiriami jau esamiems ar naujai atpažintiems žmonėms.", "facial_recognition_job_description": "Aptiktų veidų atpažinimas ir priskyrimas žmonėms. Šis darbas vykdomas pasibaigus \"veidų aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"Trūkstami\" apdoroja jokiam žmogui dar nepriskirtus aptiktus veidus.", - "failed_job_command": "Darbo {job} komanda {command} nepavyko", + "failed_job_command": "Užduoties {job} komanda {command} nepavyko", "force_delete_user_warning": "ĮSPĖJIMAS: Šis veiksmas iš karto pašalins naudotoją ir visą jo informaciją. Šis žingsnis nesugrąžinamas ir failų nebus galima atkurti.", "image_format": "Formatas", "image_format_description": "WebP sukuria mažesnius failus nei JPEG, tačiau lėčiau juos apdoroja.", + "image_fullsize_description": "Pilno dydžio nuotrauka be meta duomenų naudojama priartinus", "image_fullsize_enabled": "Įgalinti pilno dydžio nuotraukų generavimą", + "image_fullsize_enabled_description": "Generuoti viso dydžio vaizdą neinternetui pritaikytiems formatams. Kai įjungta parinktis „Pirmenybė įterptai peržiūrai“, įterptosios peržiūros naudojamos tiesiogiai be konvertavimo. Tai neturi įtakos internetui pritaikytiems formatams, pvz., JPEG", "image_fullsize_quality_description": "Pilno dydžio nuotraukų kokybė 1-100. Didesnė yra geresnė, tačiau sukuria didesniu failus.", "image_fullsize_title": "Pilno dydžio nuotraukų Nustatymai", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą peržiūrą", + "image_prefer_embedded_preview_setting_description": "Naudokite įterptąsias peržiūras RAW nuotraukose kaip įvestį vaizdų apdorojimui ir, jei įmanoma, tai gali suteikti tikslesnes kai kurių vaizdų spalvas, tačiau peržiūros kokybė priklauso nuo fotoaparato, todėl vaizde gali būti daugiau glaudinimo artefaktų.", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", + "image_prefer_wide_gamut_setting_description": "Miniatiūroms naudokite „Display P3“. Taip geriau išsaugomas vaizdų, turinčių plačias spalvų erdves, ryškumas, tačiau senesniuose įrenginiuose su senesne naršyklės versija vaizdai gali atrodyti kitaip. sRGB vaizdai išsaugomi kaip sRGB, kad būtų išvengta spalvų pasikeitimo.", "image_preview_description": "Vidutinio dydžio vaizdas su išvalytais metaduomenimis, naudojamas kai žiūrimas vienas objektas arba mašininiam mokymuisi", "image_preview_quality_description": "Peržiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet sukuriami didesni failai gali sumažinti programos reagavimo laiką. Mažos vertės nustatymas gali paveikti mašininio mokymo kokybę.", "image_preview_title": "Peržiūros nustatymai", @@ -83,11 +90,11 @@ "image_thumbnail_quality_description": "Miniatiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet pagaminami didesni failai ir gali būti sulėtintas programos reagavimo greitis.", "image_thumbnail_title": "Miniatiūros nustatymai", "job_concurrency": "{job} lygiagretumas", - "job_created": "Darbas sukurtas", - "job_not_concurrency_safe": "Šis darbas nėra saugus apdoroti lygiagrečiai.", - "job_settings": "Darbų nustatymai", - "job_settings_description": "Keisti darbų lygiagretumą", - "job_status": "Darbų būsenos", + "job_created": "Užduotis sukurta", + "job_not_concurrency_safe": "Ši užduotis nėra saugi apdoroti lygiagrečiai.", + "job_settings": "Užduočių nustatymai", + "job_settings_description": "Keisti užduočių lygiagretumą", + "job_status": "Užduočių būsenos", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka ištrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", @@ -104,6 +111,7 @@ "logging_level_description": "Įjungus, kokį žurnalo vedimo lygį naudot.", "logging_settings": "Žurnalo vedimas", "machine_learning_clip_model": "CLIP modelis", + "machine_learning_clip_model_description": "Pavadinimas CLIP modelio įvardintio here. Dėmesio, keičiant modelį jūs privalote iš naujo paleisti 'Išmaniosios Paieškos' užduotį visiems vaizdams.", "machine_learning_duplicate_detection": "Dublikatų aptikimas", "machine_learning_duplicate_detection_enabled": "Įjungti dublikatų aptikimą", "machine_learning_duplicate_detection_enabled_description": "Jei išjungta, visiškai identiški elementai vis tiek bus deduplikuoti.", @@ -113,12 +121,15 @@ "machine_learning_facial_recognition": "Veidų atpažinimas", "machine_learning_facial_recognition_description": "Aptikti, atpažinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "Veidų atpažinimo modelis", + "machine_learning_facial_recognition_model_description": "Modeliai išvardinti apimties mažėjančia tvarka. Didieji modeliai yra lėti ir naudoja daugiau atminties, tačiau sukuria geresnius rezultatus. Pastebime kad keičiant modelį jūs turite iš naujo paleisti Veidų Atpažinimo užduotį visiems vaizdams.", "machine_learning_facial_recognition_setting": "Įgalinti veidų atpažinimą", "machine_learning_facial_recognition_setting_description": "Išjungus, vaizdai nebus užšifruoti veidų atpažinimui ir nebus naudojami Žmonių sekcijoje Naršymo puslapyje.", "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "Didžiausias atstumas tarp dviejų vaizdų, kad jie būtų laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatų, tačiau gali būti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpažinimo atstumas", + "machine_learning_max_recognition_distance_description": "Didžiausias skirtumas tarp veidų, kad būtų užskaityti kaip vienas ir tas pats asmuo, rėžis nuo 0-2. Mažinant tai gali apsaugoti nuo dviejų žmonių žymėjimo tuo pačiu asmeniu, didinant tai gali apsaugoti nuo to pačio asmens žymėjimo kaip du skirtingus žmones. Pastebime kad yra paprasčiau apjungti kelių žmonių modelius į vieną nei vieną išdalinti į du, taigi kai įmanoma geriau naudoti mažensę ribą.", "machine_learning_min_detection_score": "Minimalus aptikimo balas", + "machine_learning_min_detection_score_description": "Minimalus užtikrintumo balas veido aptikimui nuo 0-1. Mažesnė reikšmė aptiks daugiau veidų tačiau bus ir daugiau klaidingų teigiamų režultatų.", "machine_learning_min_recognized_faces": "Mažiausias atpažintų veidų skaičius", "machine_learning_min_recognized_faces_description": "Mažiausias atpažintų veidų skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpažinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas žmogui nepriskirtas.", "machine_learning_settings": "Mašininio mokymosi nustatymai", @@ -151,10 +162,14 @@ "metadata_faces_import_setting_description": "Importuoti veidus iš vaizdo EXIF duomenų ir papildomų failų", "metadata_settings": "Metaduomenų nustatymai", "metadata_settings_description": "Tvarkyti metaduomenų nustatymus", - "migration_job": "Migracija", + "migration_job": "Tvarkymas", + "migration_job_description": "Pertvarkytį turinio ir veidų miniatiūras pagal naują struktūrą", + "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpažinimą naujai aptiktiems veidams", + "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", + "nightly_tasks_database_cleanup_setting": "Duomenų bazės valymo darbai", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", - "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti saugyklos etiketę seniau įkeltiems ištekliams, paleiskite", + "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos Žymą seniau įkeltiems ištekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "Iš adreso", "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdžiui: \"Immich Photo Server \"", @@ -177,20 +192,29 @@ "oauth_auto_register": "Automatinis registravimas", "oauth_auto_register_description": "Automatiškai užregistruoti naujus naudotojus po prisijungimo per OAuth", "oauth_button_text": "Mygtuko tekstas", + "oauth_client_secret_description": "Privalomas jei PKCE (Proof Key for Code Exchange) nepalaikomas pagal OAuth tiekėją", "oauth_enable_description": "Prisijungti su OAuth", "oauth_mobile_redirect_uri": "Mobiliojo peradresavimo URI", "oauth_mobile_redirect_uri_override": "Mobiliojo peradresavimo URI pakeitimas", "oauth_mobile_redirect_uri_override_description": "Įjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip ''{callback}''", + "oauth_role_claim": "Rolės Tvirtinimas", + "oauth_role_claim_description": "Suteikti admin teises automatiškai pagal šios rolės tvirtinimo buvimą. Tvirtinimas gali turėti priskirtus arba 'vartotoją' arba 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie šią funkciją galite paskaityti dokumentacijoje.", + "oauth_storage_label_claim": "Saugyklos Žyma pagal tvirtinimą", + "oauth_storage_label_claim_description": "Priskirti Saugyklos Žymą automatiškai pagal reikšmę vartotojo tvirtinime.", + "oauth_storage_quota_claim": "Saugyklos apimties tvirtinimas", + "oauth_storage_quota_claim_description": "Priskirti vartotojo saugyklos apimties kvotą automatiškai pagal šio tvirtinimo reikšmę.", "oauth_storage_quota_default": "Numatyta atminties kvota (GiB)", + "oauth_storage_quota_default_description": "Nustatoma appimties kvota GiB kai nėra nurodyta tvirtinime.", "oauth_timeout": "Užklausa viršijo laiko limitą", "oauth_timeout_description": "Laiko limitas užklausoms milisekundėmis", "password_enable_description": "Prisijungti su el. paštu ir slaptažodžiu", "password_settings": "Prisijungimas slaptažodžiu", "password_settings_description": "Tvarkyti prisijungimo slaptažodžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", + "person_cleanup_job": "Išvalyti asmenis", "quota_size_gib": "Kvotos dydis (GiB)", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", "registration": "Administratoriaus registracija", @@ -199,7 +223,7 @@ "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "reset_settings_to_recent_saved": "Nustatymų atstatymas į neseniai išsaugotus nustatymus", "scanning_library": "Biblioteka skenuojama", - "search_jobs": "Ieškoma darbų…", + "search_jobs": "Ieškoma užduočių…", "send_welcome_email": "Siųsti sveikinimo el. laišką", "server_external_domain_settings": "Išorinis domenas", "server_external_domain_settings_description": "Bendrinimo nuorodų domenas, įskaitant http(s)://", @@ -209,19 +233,36 @@ "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo pranešimas", "server_welcome_message_description": "Žinutė, rodoma prisijungimo puslapyje.", + "sidecar_job": "Sidecar metaduomenys", + "sidecar_job_description": "Aptikti ar sinchronizuoti sidecar metaduomenis iš failų sistemos", "slideshow_duration_description": "Sekundžių skaičius, kiek viena nuotrauka rodoma", "smart_search_job_description": "Vykdykite mašininį mokymąsi bibliotekos elementų išmaniajai paieškai", "storage_template_date_time_description": "Elemento sukūrimo laiko žymė yra naudojama laiko informacijai", "storage_template_date_time_sample": "Pavyzdinis laikas {date}", + "storage_template_enable_description": "Aktyvuoti saugyklos šabloną", + "storage_template_hash_verification_enabled": "Aktyvuoti Hash tikrinimą", + "storage_template_hash_verification_enabled_description": "Aktyvuojamas Hash tikrinimas, neišjungti nebent gerai suprantate galimas pasekmes", + "storage_template_migration": "Saugyklos tvarkymas pagal šabloną", + "storage_template_migration_description": "Taikyti dabartinį {template} anksčiau įkeltiems duomenims", + "storage_template_migration_info": "Saugyklos tvarkyklė konvertuos visus plėtinius mažosiomis raidėmis. Šablonas bus taikomas tik naujiems duomenims. Taikyti šabloną retroaktyviai anksčiau įkeltiems duomenims, paleiskite šią {job}.", + "storage_template_migration_job": "Saugyklos Tvarkymo Pagal Šabloną Užduotis", + "storage_template_more_details": "Daugiau detalių apie šią funkciją, atsižvelkite į Storage Template ir jo galimus implications", + "storage_template_onboarding_description_v2": "Kai aktyvuota, ši funkcija automatiškai sukurs failus pagal vartotojo-nustatytą šabloną. Daugiau informacijos, prašome skaityti documentation.", + "storage_template_path_length": "Preliminarus struktūros kelio ilgis/limitas:{length, number}/{limit, number}", + "storage_template_settings": "Saugyklos Šablonas", + "storage_template_settings_description": "Tvarkyti aplankų struktūrą bei failų pavadinimus įkeliamiems duomenims", + "storage_template_user_label": "{label} yra vartotojo Saugyklos Žymą", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "Žymų išvalymas", + "template_email_available_tags": "Savo šablone galite naudoti nurodytas kintamas reikšmes:{tags}", + "template_email_if_empty": "Jei šablone tuščia reikšmė, bus naudojamas numatytas pagal nutylėjimą El. pašto adresas.", "template_email_preview": "Peržiūra", "template_email_settings": "El. pašto Šablonai", "template_settings": "Pranešimų šablonai", "template_settings_description": "Tvarkyti pasirinktinius pranešimų šablonus", "theme_custom_css_settings": "Individualizuotas CSS", "theme_settings": "Temos nustatymai", - "thumbnail_generation_job": "Generuoti miniatiūras", + "thumbnail_generation_job": "Generuoti Miniatiūras", "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", @@ -233,16 +274,21 @@ "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", + "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikšmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo mažesnis tuo kokybiškesnis tačiau didesni failai.", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", "transcoding_max_bitrate": "Maksimalus bitų srautas", + "transcoding_max_bitrate_description": "Pasirenkant max bitrate galima pasiekti labiau nuspėjamą failų dydį su minimaliais kokybės praradimais. Prie 720p, tipinės reikšmės yra 2600 kbits/s jei BP9 ar HVEC, arba 4500 kbits/s jei H.264. Neveiksnus jei pasirenkamas 0.", + "transcoding_preset_preset_description": "Kompresijos greitis. Siekiant tam tikro bitrate lėtesnis apdorojimas lems mažesnius failų dydžius ir padidins kokybę. VP9 ignoruos greičius virš \"gretesnis\" lygio.", "transcoding_target_resolution_description": "Didesnės skiriamosios gebos gali išsaugoti daugiau detalių, tačiau jas koduoti užtrunka ilgiau, failų dydžiai yra didesni ir gali sumažėti programos jautrumas.", "transcoding_video_codec": "Video kodekas", "trash_enabled_description": "Įgalinti šiukšliadėžės funkcijas", "trash_number_of_days": "Dienų skaičius", "trash_settings": "Šiukšliadėžės nustatymai", "trash_settings_description": "Tvarkyti šiukšliadėžės nustatymus", + "user_cleanup_job": "Vartotojų išvalymas", "user_delete_delay_settings": "Ištrynimo delsa", + "user_delete_delay_settings_description": "Skaičius dienų po ištrynimo kuomet vartotojo paskyrą ir susiję duomenys bus negražinamai ištrinti. Vartotojo Trynimo užduotis paleidžiama vidurnaktį ir tikrina kurie vartotojai gali būti trinami. Šio nustatymo pakeitimai bus naudojami sekančio užduoties paleidimo metu.", "user_management": "Naudotojų valdymas", "user_password_has_been_reset": "Naudotojo slaptažodis buvo iš naujo nustatytas:", "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", @@ -305,7 +351,6 @@ "assets": "Elementai", "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementų}}", "assets_added_to_album_count": "Į albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", - "assets_added_to_name_count": "Į {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementų perkelta}} į šiukšliadėžę", "assets_permanently_deleted_count": "{count, plural, one {# elementas ištrintas} few {# elementai ištrinti} other {# elementų ištrinta}} visam laikui", @@ -316,7 +361,11 @@ "authorized_devices": "Autorizuoti įrenginiai", "back": "Atgal", "back_close_deselect": "Atgal, uždaryti arba atžymėti", + "backup_background_service_current_upload_notification": "Įkeliamas {filename}", + "backup_background_service_upload_failure_notification": "Nepavyko įkelti {filename}", "backup_controller_page_background_wifi": "Only on WiFi", + "backup_controller_page_filename": "Failo pavadinimas: {filename}[{size}]", + "backup_controller_page_uploading_file_info": "Įkeliama failo info", "birthdate_saved": "Sėkmingai išsaugota gimimo data", "blurred_background": "Neryškus fonas", "bugs_and_feature_requests": "Klaidų ir funkcijų užklausos", @@ -345,6 +394,7 @@ "clear_all": "Išvalyti viską", "clear_message": "Išvalyti pranešimą", "clear_value": "Išvalyti reikšmę", + "client_cert_invalid_msg": "Netinkamas sertifikato failas arba neteisingas slaptažodis", "close": "Uždaryti", "collapse": "Suskleisti", "collapse_all": "Suskleisti viską", @@ -420,8 +470,11 @@ "do_not_show_again": "Daugiau nerodyti šio pranešimo", "documentation": "Dokumentacija", "download": "Atsisiųsti", + "download_include_embedded_motion_videos_description": "Pridėti prie judesio nuotraukų įterptus video kaip atskirą failą", "download_settings": "Atsisiųsti", "downloading": "Siunčiama", + "downloading_asset_filename": "Parsisiunčiamas resursas {filename}", + "drop_files_to_upload": "Užkelkite failus bet kurioje vietoje kad įkeltumėte", "duplicates": "Dublikatai", "duplicates_description": "Sutvarkykite kiekvieną elementų grupę nurodydami elementus, kurie yra dublikatai (jei tokių yra)", "duration": "Trukmė", @@ -493,6 +546,7 @@ "unable_to_delete_import_path": "Nepavyksta ištrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyko ištrinti bendrinimo nuorodos", "unable_to_delete_user": "Nepavyksta ištrinti naudotojo", + "unable_to_download_files": "Nepavyksta atsisiųsti failų", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti išimčių šablono", "unable_to_edit_import_path": "Nepavyksta redaguoti išimčių kelio", "unable_to_enter_fullscreen": "Nepavyksta pereiti į viso ekrano režimą", @@ -517,6 +571,7 @@ "unable_to_scan_library": "Nepavyksta nuskaityti bibliotekos", "unable_to_set_feature_photo": "Nepavyksta nustatyti mėgstamiausios nuotraukos", "unable_to_set_profile_picture": "Nepavyksta nustatyti profilio nuotraukos", + "unable_to_submit_job": "Napvyko sukurti užduoties", "unable_to_trash_asset": "Nepavyko perkelti į šiukšliadėžę", "unable_to_upload_file": "Nepavyksta įkelti failo" }, @@ -539,6 +594,7 @@ "features_setting_description": "Valdyti aplikacijos funkcijas", "file_name": "Failo pavadinimas", "file_name_or_extension": "Failo pavadinimas arba plėtinys", + "filename": "Failopavadinimas", "filetype": "Failo tipas", "filter_people": "Filtruoti žmones", "folders": "Aplankai", @@ -574,8 +630,9 @@ }, "invite_people": "Kviesti žmones", "invite_to_album": "Pakviesti į albumą", + "ios_debug_info_no_sync_yet": "Jokia background sync užduotis dar nebuvo paleista", "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", - "jobs": "Darbai", + "jobs": "Užduotys", "keep": "Palikti", "keep_all": "Palikti visus", "keyboard_shortcuts": "Spartieji klaviatūros klavišai", @@ -660,6 +717,7 @@ "no_results": "Nerasta", "no_results_description": "Pabandykite sinonimą arba bendresnį raktažodį", "not_in_any_album": "Nė viename albume", + "note_apply_storage_label_to_previously_uploaded assets": "Pastaba: Priskirti Saugyklos Žymą prie ankčiau įkeltų ištekliu, paleiskite šį", "notes": "Pastabos", "notification_toggle_setting_description": "Įjungti el. pašto pranešimus", "notifications": "Pranešimai", @@ -707,6 +765,14 @@ "place": "Vieta", "places": "Vietos", "play_memories": "Leisti atsiminimus", + "profile": "Profilis", + "profile_drawer_app_logs": "Logai", + "profile_drawer_client_out_of_date_major": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_client_out_of_date_minor": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", + "profile_drawer_client_server_up_to_date": "Klientas ir Serveris yra atnaujinti", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_server_out_of_date_minor": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", "profile_image_of_user": "{user} profilio nuotrauka", "profile_picture_set": "Profilio nuotrauka nustatyta.", "public_album": "Viešas albumas", @@ -893,7 +959,7 @@ "show_albums": "Rodyti albumus", "show_all_people": "Rodyti visus asmenis", "show_and_hide_people": "Rodyti ir paslėpti žmones", - "show_file_location": "Rodyti rinkmenos vietą", + "show_file_location": "Rodyti failo vietą", "show_gallery": "Rodyti galeriją", "show_hidden_people": "Rodyti paslėptus asmenis", "show_in_timeline": "Rodyti laiko skalėje", @@ -936,6 +1002,7 @@ "status": "Statusas", "stop_casting": "Nutraukti transliavimą", "storage": "Saugykla", + "storage_label": "Saugyklos Žyma", "storage_usage": "Naudojama {used} iš {available}", "submit": "Pateikti", "suggestions": "Pasiūlymai", diff --git a/i18n/lv.json b/i18n/lv.json index 3fcf228612..01a506a90a 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -88,11 +88,14 @@ "machine_learning_url_description": "Mašīnmācīšanās servera URL", "manage_concurrency": "Vienlaicīgas darbības pārvaldība", "manage_log_settings": "Žurnāla iestatījumu pārvaldība", + "map_dark_style": "Tumšais stils", "map_gps_settings": "Kartes un GPS iestatījumi", "map_gps_settings_description": "Karšu un GPS (apgrieztās ģeokodēšanas) iestatījumu pārvaldība", + "map_light_style": "Gaišais stils", "map_manage_reverse_geocoding_settings": "Reversās ģeokodēšanas iestatījumu pārvaldība", "map_settings": "Karte", "map_settings_description": "Kartes iestatījumu pārvaldība", + "map_style_description": "URL uz style.json kartes tēmu", "metadata_extraction_job": "Metadatu iegūšana", "metadata_settings": "Metadatu iestatījumi", "metadata_settings_description": "Metadatu iestatījumu pārvaldība", @@ -110,6 +113,10 @@ "notification_email_test_email_sent": "Uz {email} ir nosūtīts testa e-pasts. Lūdzu, pārbaudi savu iesūtni.", "notification_settings": "Paziņojumu iestatījumi", "notification_settings_description": "Paziņojumu iestatījumu, tostarp e-pasta, pārvaldība", + "oauth_auto_launch": "Palaist automātiski", + "oauth_auto_launch_description": "Pie navigācijas uz pieslēgšanās lapu automātiski uzsākt OAuth pieslēgšanās plūsmu", + "oauth_auto_register": "Automātiska reģistrācija", + "oauth_auto_register_description": "Pēc pieslēgšanās ar OAuth automātiski reģistrēt jaunus lietotājus", "oauth_button_text": "Pogas teksts", "oauth_enable_description": "Pieslēgties ar OAuth", "oauth_settings": "OAuth", @@ -124,6 +131,7 @@ "require_password_change_on_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "scanning_library": "Skenē bibliotēku", "search_jobs": "Meklēt uzdevumus…", + "server_public_users": "Publiski lietotāji", "server_settings": "Servera iestatījumi", "server_settings_description": "Servera iestatījumu pārvaldība", "server_welcome_message": "Sveiciena ziņa", @@ -134,7 +142,12 @@ "storage_template_path_length": "Aptuvenais ceļa garuma ierobežojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", "system_settings": "Sistēmas iestatījumi", + "template_email_available_tags": "Sagatavē var izmantot šos mainīgos: {tags}", + "template_email_if_empty": "Ja sagatave ir tukša, tiks izmantots noklusējuma e-pasts.", + "template_email_invite_album": "Albuma ielūguma sagatave", "template_email_preview": "Priekšskatījums", + "template_email_settings": "E-pasta sagataves", + "template_email_update_album": "Atjaunināt albuma sagatavi", "template_settings_description": "Pielāgotu paziņojumu veidņu pārvaldība", "theme_custom_css_settings": "Pielāgots CSS", "theme_custom_css_settings_description": "Cascading Style Sheets ļauj pielāgot Immich izskatu.", @@ -219,7 +232,9 @@ "are_these_the_same_person": "Vai šī ir tā pati persona?", "asset_action_delete_err_read_only": "Nevar dzēst read only aktīvu(-s), notiek izlaišana", "asset_action_share_err_offline": "Nevar iegūt bezsaistes aktīvu(-s), notiek izlaišana", + "asset_added_to_album": "Pievienots albumam", "asset_adding_to_album": "Pievieno albumam…", + "asset_description_updated": "Faila apraksts ir atjaunināts", "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", @@ -228,6 +243,7 @@ "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi", "asset_list_settings_title": "Fotorežģis", + "asset_uploaded": "Augšupielādēts", "asset_uploading": "Augšupielādē…", "asset_viewer_settings_title": "Aktīvu Skatītājs", "assets": "aktīvi", @@ -339,6 +355,7 @@ "clear": "Notīrīt", "clear_all": "Notīrīt visu", "clear_value": "Notīrīt vērtību", + "client_cert_subtitle": "Atbalsta tikai PKCS12 (.p12, .pfx) formātu. Sertifikātu importēšana/noņemšana ir pieejama tikai pirms pieslēgšanās", "client_cert_title": "SSL klienta sertifikāts", "clockwise": "Pulksteņrādītāja virzienā", "close": "Aizvērt", @@ -504,7 +521,11 @@ "features_setting_description": "Lietotnes funkciju pārvaldība", "filename": "Faila nosaukums", "filetype": "Faila tips", + "folder": "Mape", + "folder_not_found": "Mape nav atrasta", "folders": "Mapes", + "gcast_enabled": "Google Cast", + "get_help": "Saņemt palīdzību", "haptic_feedback_switch": "Iestatīt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", @@ -529,10 +550,12 @@ "hour": "Stunda", "id": "ID", "image": "Attēls", + "image_saved_successfully": "Attēls saglabāts", "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "Kopīgošanas Kļūda", "immich_logo": "Immich logo", + "immich_web_interface": "Immich tīmekļa saskarne", "import_from_json": "Importēt no JSON", "import_path": "Importa ceļš", "in_albums": "{count, plural, one {# albumā} other {# albumos}}", @@ -545,18 +568,25 @@ "night_at_midnight": "Katru dienu pusnaktī", "night_at_twoam": "Katru dienu 2.00 naktī" }, + "invalid_date": "Nederīgs datums", + "invalid_date_format": "Nederīgs datuma formāts", "invite_people": "Ielūgt cilvēkus", "invite_to_album": "Uzaicināt albumā", + "ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}", + "ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu", "jobs": "Uzdevumi", "keep": "Paturēt", "keep_all": "Paturēt visus", + "keep_this_delete_others": "Paturēt šo, dzēst citus", "keyboard_shortcuts": "Tastatūras saīsnes", "language": "Valoda", + "language_search_hint": "Meklēt valodas...", "language_setting_description": "Izvēlieties vēlamo valodu", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Ģeogrāfiskais platums", "leave": "Paturēt", + "lens_model": "Objektīva modelis", "let_others_respond": "Ļaut citiem atbildēt", "level": "Līmenis", "library": "Bibliotēka", @@ -600,7 +630,7 @@ "longitude": "Ģeogrāfiskais garums", "look": "Izskats", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaļu skatītājā.", - "make": "Firma", + "make": "Ražotājs", "manage_shared_links": "Kopīgoto saišu pārvaldība", "manage_sharing_with_partners": "Koplietošanas ar partneriem pārvaldība", "manage_the_app_settings": "Lietotnes iestatījumu pārvaldība", @@ -738,48 +768,78 @@ "permission_onboarding_request": "Immich nepieciešama atļauja skatīt jūsu fotoattēlus un videoklipus.", "person": "Persona", "photos": "Fotoattēli", + "photos_and_videos": "Fotogrāfijas un video", "photos_from_previous_years": "Fotogrāfijas no iepriekšējiem gadiem", + "pick_a_location": "Izvēlies atrašanās vietu", "pin_verification": "PIN koda pārbaude", "place": "Atrašanās vieta", "places": "Vietas", + "play": "Atskaņot", + "play_memories": "Atskaņot atmiņas", "please_auth_to_access": "Lai piekļūtu, lūdzu, autentificējieties", "port": "Ports", "preferences_settings_title": "Iestatījumi", "preview": "Priekšskatījums", "previous": "Iepriekšējais", + "previous_memory": "Iepriekšējā atmiņa", "privacy": "Privātums", "profile": "Profils", "profile_drawer_app_logs": "Žurnāli", "profile_drawer_client_out_of_date_major": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_client_out_of_date_minor": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko papildversiju.", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko papildversiju.", + "profile_image_of_user": "{user} profila attēls", + "profile_picture_set": "Profila attēls iestatīts.", + "public_album": "Publisks albums", "purchase_account_info": "Atbalstītājs", + "purchase_activated_subtitle": "Paldies, ka atbalstāt Immich un atvērtā koda programmatūru", + "purchase_activated_time": "Aktivizēts {date}", + "purchase_activated_title": "Tava atslēga ir sekmīgi aktivizēta", + "purchase_button_activate": "Aktivizēt", "purchase_button_buy": "Pirkt", + "purchase_button_buy_immich": "Iegādāties Immich", "purchase_button_never_show_again": "Nekad vairs nerādīt", "purchase_button_reminder": "Atgādināt man pēc 30 dienām", "purchase_button_remove_key": "Noņemt atslēgu", "purchase_button_select": "Izvēlēties", + "purchase_failed_activation": "Neizdevās aktivizēt! Lūdzu, pārbaudi savu e-pastu, lai iegūtu pareizo produkta atslēgu!", "purchase_individual_description_2": "Atbalstītāja statuss", "purchase_input_suggestion": "Vai tev ir produkta atslēga? Ievadi atslēgu zemāk", "purchase_license_subtitle": "Nopērc Immich licenci, lai atbalstītu turpmāku pakalpojuma attīstību", "purchase_lifetime_description": "Pirkums uz mūžu", "purchase_option_title": "IEGĀDES IESPĒJAS", + "purchase_panel_info_1": "Immich veidošanai ir nepieciešams daudz laika un pūļu, un pie tā strādā pilna laika inženieri, lai padarītu to pēc iespējas labāku. Mūsu misija ir panākt, lai atvērtā koda programmatūra un ētiska uzņēmējdarbības prakse kļūtu par ilgtspējīgu ienākumu avotu izstrādātājiem un izveidotu privātumu respektējošu ekosistēmu ar reālām alternatīvām ekspluatējošiem mākoņpakalpojumiem.", + "purchase_panel_info_2": "Tā kā mēs esam apņēmušies nepievienot maksas funkcionalitāti, šis pirkums nepiešķirs jums nekādas papildu Immich funkcijas. Mēs paļaujamies uz tādiem lietotājiem kā jūs, lai atbalstītu nepārtrauktu Immich attīstību.", "purchase_panel_title": "Atbalsti projektu", "purchase_remove_product_key": "Noņemt produkta atslēgu", + "purchase_remove_product_key_prompt": "Vai tiešām vēlaties noņemt produkta atslēgu?", "purchase_remove_server_product_key": "Noņemt servera produkta atslēgu", + "purchase_remove_server_product_key_prompt": "Vai tiešām vēlaties noņemt Servera produkta atslēgu?", "purchase_server_description_1": "Visam serverim", "purchase_server_description_2": "Atbalstītāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", "rating_clear": "Noņemt vērtējumu", + "rating_description": "Rādīt EXIF vērtējumu informācijas panelī", + "reaction_options": "Reakcijas iespējas", "read_changelog": "Lasīt izmaiņu sarakstu", "recently_added_page_title": "Nesen Pievienotais", + "refresh_thumbnails": "Atsvaidzināt sīktēlus", + "refreshed": "Atsvaidzināts", "remove": "Noņemt", + "remove_assets_title": "Izņemt failus?", + "remove_deleted_assets": "Izņemt dzēstos failus", "remove_from_album": "Noņemt no albuma", + "remove_from_album_action_prompt": "No albuma izņemti {count} faili", "remove_from_favorites": "Noņemt no izlases", + "remove_from_lock_folder_action_prompt": "No slēgtās mapes izņemti {count} faili", "remove_from_locked_folder": "Izņemt no slēgtās mapes", + "remove_memory": "Noņemt atmiņu", + "remove_photo_from_memory": "Noņemt fotogrāfiju no šīs atmiņas", + "remove_url": "Noņemt URL", "remove_user": "Noņemt lietotāju", "removed_api_key": "Noņēma API atslēgu: {name}", "removed_from_archive": "Noņēma no arhīva", @@ -790,6 +850,11 @@ "replace_with_upload": "Aizstāt ar augšupielādi", "require_user_to_change_password_on_first_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "rescan": "Pārskenēt atkārtoti", + "reset": "Atiestatīt", + "reset_password": "Atiestatīt paroli", + "reset_people_visibility": "Atiestatīt cilvēku redzamību", + "reset_pin_code": "Atiestatīt PIN kodu", + "reset_to_default": "Atiestatīt noklusējuma iestatījumus", "resolve_duplicates": "Atrisināt dublēšanās gadījumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", @@ -802,6 +867,7 @@ "role_editor": "Redaktors", "role_viewer": "Skatītājs", "save": "Saglabāt", + "save_to_gallery": "Saglabāt galerijā", "saved_api_key": "API atslēga saglabāta", "saved_profile": "Profils saglabāts", "saved_settings": "Iestatījumi saglabāti", @@ -813,9 +879,23 @@ "scanning_for_album": "Skenē albumu...", "search": "Meklēt", "search_albums": "Meklēt albumus", + "search_by_context": "Meklēt pēc konteksta", + "search_by_description": "Meklēt pēc apraksta", + "search_by_description_example": "Pārgājiens Līgatnē", + "search_by_filename": "Meklēt pēc faila nosaukuma vai paplašinājuma", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", + "search_camera_make": "Meklēt pēc fotokameras ražotāja...", + "search_camera_model": "Meklēt pēc fotokameras modeļa...", + "search_city": "Meklēt pēc pilsētas...", + "search_country": "Meklēt pēc valsts...", "search_filter_apply": "Lietot filtru", + "search_filter_camera_title": "Izvēlies fotokameras veidu", + "search_filter_date": "Datums", "search_filter_display_option_not_in_album": "Nav albumā", + "search_filter_filename": "Meklēt pēc faila nosaukuma", + "search_filter_location": "Atrašanās vieta", + "search_filter_location_title": "Izvēlies atrašanās vietu", + "search_for_existing_person": "Meklēt esošu personu", "search_no_people": "Nav cilvēku", "search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"", "search_page_categories": "Kategorijas", @@ -836,6 +916,7 @@ "second": "Sekunde", "select_album_cover": "Izvēlieties albuma vāciņu", "select_all_duplicates": "Atlasīt visus dublikātus", + "select_from_computer": "Izvēlēties no datora", "select_photos": "Fotoattēlu Izvēle", "select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu", "server_info_box_app_version": "Aplikācijas Versija", @@ -1031,6 +1112,7 @@ "viewer_stack_use_as_main_asset": "Izmantot kā Galveno Aktīvu", "viewer_unstack": "At-Stekot", "waiting": "Gaida", + "warning": "Brīdinājums", "week": "Nedēļa", "welcome": "Laipni lūgti", "welcome_to_immich": "Laipni lūgti Immich", diff --git a/i18n/mn.json b/i18n/mn.json index 8a18a1d5e5..85092fef0d 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -15,10 +15,13 @@ "add_a_name": "Нэр өгөх", "add_a_title": "Гарчиг оруулах", "add_endpoint": "Endpoint нэмэх", + "add_import_path": "Импортлох зам нэмэх", "add_location": "Байршил оруулах", "add_more_users": "Өөр хэрэглэгчид нэмэх", "add_partner": "Хамтрагч нэмэх", + "add_path": "Зам нэмэх", "add_photos": "Зураг нэмэх", + "add_tag": "Шошго нэмэх", "add_to_album": "Цомогт оруулах", "add_to_album_bottom_sheet_added": "{album}-д нэмлээ", "add_to_album_bottom_sheet_already_exists": "{album}-д аль хэдийн орсон байна", @@ -28,6 +31,7 @@ "added_to_favorites": "Дуртай зурганд нэмэх", "added_to_favorites_count": "Дуртай зурагнуудад {count, number} нэмэгдлээ", "admin": { + "admin_user": "Админ хэрэглэгч", "authentication_settings": "Танин нэвтрэлт тохиргоо", "authentication_settings_description": "Нууц үгийн удирдлага, OAuth болон бусад танин нэвтрэлтийн тохиргоо", "authentication_settings_disable_all": "Бүх нэвтрэх аргуудыг идэвхигүй болгохдоо итгэлтэй байна уу? Нэвтрэх үйлдэл бүрэн идэвхигүй болно.", @@ -35,11 +39,15 @@ "backup_database": "Өгөгдлийн сангийн дамп үүсгэх", "backup_database_enable_description": "Өгөгдлийн сангийн дамп идэвхижүүлэх", "backup_keep_last_amount": "Өмнөх хэдэн дампыг хадгалах вэ", + "backup_settings": "Датабаз дамп тохиргоо", + "backup_settings_description": "Датабазаас дамп хийх тохиргоонууд.", "config_set_by_file": "Тохиргоог одоогоор файлаас авч байна", "confirm_delete_library": "Та {library} гэсэн санг устгахдаа итгэлтэй байна уу?", "confirm_delete_library_assets": "Та энэ санг устгахдаа итгэлтэй байна уу? Энэ үйлдлээр таны {count, plural, one {# contained asset} other {all # contained assets}} серверээс устах бөгөөд буцаах боломжгүй. Гэхдээ файлууд диск дээрээ үлдэнэ.", "confirm_email_below": "Баталгаажуулахын тулд та \"{email}\" гэж бичнэ үү", "confirm_reprocess_all_faces": "Бүх царайг дахин процесс хийх үү? Тэгвэл бүх нэрс арилах болно.", + "confirm_user_password_reset": "{user}-ийн нууц үгийг дахин тохируулах уу?", + "confirm_user_pin_code_reset": "{user} хэрэглэгчийн PIN code дахин тохируулах уу?", "face_detection": "Нүүр илрүүлэх", "image_quality": "Чанар", "job_settings": "Ажлын тохиргоо", diff --git a/i18n/mr.json b/i18n/mr.json index 6c2ab7406c..781174c79e 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -4,6 +4,7 @@ "account_settings": "खाते व्यवस्था", "acknowledge": "मान्यता", "action": "कृती", + "action_common_update": "अद्ययावत", "actions": "कृत्ये", "active": "सक्रिय", "activity": "गतिविधि", @@ -13,6 +14,7 @@ "add_a_location": "एक स्थळ टाका", "add_a_name": "नाव टाका", "add_a_title": "शीर्षक टाका", + "add_endpoint": "एंडपॉइंट जोडा", "add_exclusion_pattern": "अपवाद नमुना जोडा", "add_import_path": "आयात मार्ग टाका", "add_location": "स्थळ टाका", @@ -20,8 +22,11 @@ "add_partner": "भागीदार जोडा", "add_path": "मार्ग टाका", "add_photos": "छायाचित्रे जोडा", + "add_tag": "टॅग जोडा", "add_to": "त्या मध्ये जोडा…", "add_to_album": "संग्रहात टाका", + "add_to_album_bottom_sheet_added": "{album} मध्ये जोडले गेले", + "add_to_album_bottom_sheet_already_exists": "आधीच {album} मध्ये आहे", "add_to_shared_album": "सामायिक संग्रहात टाका", "add_url": "URL जोडा", "added_to_archive": "संग्रहालयात जोडले", @@ -29,6 +34,7 @@ "added_to_favorites_count": "आवडत्यात {count, number} टाकले", "admin": { "add_exclusion_pattern_description": "अपवाद अनुकूलन जोडा. ** आणि ? या उपयोगात ग्लोबिंग समर्थित आहे. कोणत्याही \"Raw\" नावाच्या निर्देशिकेमधील सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"/Raw/\" वापरा. \".tif\" या सामान्य पथावर समाप्त असलेल्या सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"**/.tif\" वापरा. विशिष्ट पथ दुर्लक्ष करण्यासाठी \"/path/to/ignore/**\" वापरा.", + "admin_user": "प्रशासन वापरकर्ता", "asset_offline_description": "ही बाह्य संग्रहालय संसाधने डिस्कवर नाहीत आणि ट्रॅशमध्ये विस्थापित केली गेली आहेत. जर फाइल संग्रहालयामध्ये विस्थापित केली गेली आहे, तर नवीन संगत संसाधन किंव्हा रोजीनिशी मध्ये तपासा. हा संसाधन वापर करण्यासाठी कृपया निम्नलिखित खतावणी पथाला इम्मीच द्वारा वापरू शकतो याची तपासणी करा आणि तो संग्रहालय चाळा.", "authentication_settings": "प्रमाणीकरण साधक", "authentication_settings_description": "परवलीचा शब्द, OAuth आणि अन्य प्रमाणीकरण प्रबंधन करा", @@ -47,6 +53,7 @@ "confirm_email_below": "पुष्टी करण्या साठी, खाली \"{email}\" टंकलिखित करा", "confirm_reprocess_all_faces": "तुम्हाला खात्री आहे का की तुम्हाला सर्व चेहऱ्यांवर पुन्हा प्रक्रिया करायची आहे? यामुळे नाव दिलेले लोकही साफ होतील.", "confirm_user_password_reset": "तुम्हाला नक्की {user} चा परवलीचा शब्द बदलायचा आहे का?", + "confirm_user_pin_code_reset": "तुम्हाला नक्की {user} चा पिन कोड रीसेट करायचा आहे का?", "create_job": "कार्य बनवा", "cron_expression": "वेळापत्रक सूत्र", "cron_expression_description": "चाळन्याचे वेळापत्रक क्रॉन पद्धती ने करा. अधिक माहिती साठी पहा: क्रॉन गुरु", @@ -55,6 +62,16 @@ "duplicate_detection_job_description": "सारख्या छायाचित्रांचा शोध घेण्यासाठी यांत्रिकी प्रशिक्षण द्या. ही कार्यक्षमता चतुर शोधप्रणालीवर अवलंबून आहे", "exclusion_pattern_description": "आपले संग्रहालय चाळताना अपवाद नमुने आपल्याला खतावण्या आणि र्निर्देशिकेला दुर्लक्षीत करू देतात. आपल्याकडे कच्च्या खतावण्या सारख्या आयात करू इच्छित नसलेल्या असंपादित (RAW) खतावण्या असलेल्या निर्देशिका असल्यास हे उपयुक्त आहे.", "external_library_management": "बाह्य संग्रहालय व्यवस्थापन", - "face_detection": "मुख संशोधन" + "face_detection": "मुख संशोधन", + "face_detection_description": "मशीन लर्निंग वापरून मालमत्तांमधील चेहरे शोधा. व्हिडिओंसाठी, फक्त थंबनेलचा विचार केला जातो. \"रिफ्रेश\" (पुन्हा) सर्व मालमत्तांवर प्रक्रिया करते. \"रीसेट\" याव्यतिरिक्त सर्व वर्तमान चेहरा डेटा साफ करते. \"गहाळ\" मालमत्तांवर अद्याप प्रक्रिया न केलेल्या रांगेत ठेवते. शोधलेले चेहरे फेस डिटेक्शन पूर्ण झाल्यानंतर फेशियल रेकग्निशनसाठी रांगेत ठेवले जातील, त्यांना विद्यमान किंवा नवीन लोकांमध्ये गटबद्ध केले जाईल.", + "facial_recognition_job_description": "शोधलेले चेहरे लोकांमध्ये गटबद्ध करा. हे चरण चेहरा शोधणे पूर्ण झाल्यानंतर चालते. \"रीसेट करा\" (पुन्हा) सर्व चेहरे क्लस्टर कर. \"गहाळ\" चेहरे रांगेत समाविष्ट करते ज्यांना नियुक्त केलेली व्यक्ती नाही.", + "failed_job_command": "{command} कमांड जॉबसाठी अयशस्वी झाला: {job}", + "force_delete_user_warning": "सावधान: हे वापरकर्ता आणि सर्व मालमत्ता ताबडतोब काढून टाकेल. हे पूर्ववत करता येणार नाही आणि फायली पुनर्प्राप्त करता येणार नाहीत.", + "image_format": "फॉरमॅट", + "image_format_description": "WebP JPEG पेक्षा लहान फायली तयार करते, परंतु एन्कोड करण्यास हळू असते.", + "image_fullsize_description": "झूम इन केल्यावर वापरल्या जाणाऱ्या स्ट्रिप केलेल्या मेटाडेटासह पूर्ण आकाराची प्रतिमा", + "image_fullsize_enabled": "पूर्ण-आकारातील प्रतिमा निर्मिती", + "image_fullsize_enabled_description": "वेब-फ्रेंडली नसलेल्या फॉरमॅटसाठी पूर्ण-आकाराची प्रतिमा तयार करा. जेव्हा \"embedded preview\" चालुआसेल तेव्हा, \"embedded preview\" थेट रूपांतरणाशिवाय वापरले जातात. JPEG सारख्या वेब-फ्रेंडली फॉरमॅटवर परिणाम होत नाही.", + "image_fullsize_quality_description": "१-१०० पर्यंत पूर्ण-आकारातील प्रतिमा गुणवत्ता. जास्त तेव्हडे चांगले, परंतु मोठ्या फायली तयार करते." } } diff --git a/i18n/ms.json b/i18n/ms.json index cfe935102e..e7e0432a4c 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -14,6 +14,7 @@ "add_a_location": "Tambah lokasi", "add_a_name": "Tambah nama", "add_a_title": "Tambah tajuk", + "add_endpoint": "Tambah titik akhir", "add_exclusion_pattern": "Tambahkan corak pengecualian", "add_import_path": "Tambahkan laluan import", "add_location": "Tambah lokasi", @@ -21,6 +22,7 @@ "add_partner": "Tambah rakan", "add_path": "Tambah laluan", "add_photos": "Tambah gambar", + "add_tag": "Tambah tag", "add_to": "Tambah ke…", "add_to_album": "Tambah ke album", "add_to_album_bottom_sheet_added": "Dimasukkan ke {album}", @@ -32,17 +34,18 @@ "added_to_favorites_count": "Menambahkan {count, number} ke kegemaran", "admin": { "add_exclusion_pattern_description": "Tambahkan corak pengecualian. Globbing menggunakan *, **, dan ? disokong. Untuk mengabaikan semua fail dalam mana-mana direktori bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua fail yang berakhir dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan laluan mutlak, gunakan \"/path/to/ignore/**\".", + "admin_user": "Pengguna Pentadbir", "asset_offline_description": "Aset pustaka luaran ini tidak lagi ditemui pada cakera dan telah dialihkan ke sampah. Jika fail telah dialihkan dalam pustaka, semak garis masa anda untuk aset baharu yang sepadan. Untuk memulihkan aset ini, sila pastikan bahawa laluan fail di bawah boleh diakses oleh Immich dan mengimbas pustaka.", "authentication_settings": "Tetapan Pengesahan", "authentication_settings_description": "Urus kata laluan, OAuth dan tetapan pengesahan lain", "authentication_settings_disable_all": "Adakah anda pasti mahu melumpuhkan semua kaedah log masuk? Log masuk akan dilumpuhkan sepenuhnya.", "authentication_settings_reenable": "Untuk menghidupkan semula, guna Arahan Pelayan.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Sandar pangkalan data", - "backup_database_enable_description": "Aktifkan sandaran pangkalan data", - "backup_keep_last_amount": "Jumlah sandaran sebelumnya yang hendak disimpan", - "backup_settings": "Tetapan Sandaran", - "backup_settings_description": "Urus tetapan sandaran pangkalan data", + "backup_database": "Buat Salinan Pangkalan Data", + "backup_database_enable_description": "Dayakan salinan pangkalan data", + "backup_keep_last_amount": "Jumlah salinan pangkalan data sebelumnya untuk disimpan", + "backup_settings": "Tetapan Salinan Pangkalan Data", + "backup_settings_description": "Urus tetapan salinan pangkalan data.", "cleared_jobs": "Kerja telah dibersihkan untuk: {job}", "config_set_by_file": "Konfigurasi kini ditetapkan oleh fail konfigurasi", "confirm_delete_library": "Adakah anda pasti mahu memadamkan {library}?", @@ -72,7 +75,7 @@ "image_fullsize_quality_description": "Kualiti imej bersaiz penuh dari 1-100. Lebih tinggi adalah lebih baik, tetapi menghasilkan fail yang lebih besar.", "image_fullsize_title": "Tetapan Imej bersaiz penuh", "image_prefer_embedded_preview": "Cadangkan pratonton terbenam", - "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input kepada pemprosesan imej apabila tersedia. Cara ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung pada kamera dan imej mungkin mempunyai lebih banyak artifak mampatan.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input untuk pemprosesan imej apabila tersedia. Ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung kepada kamera dan imej mungkin mengandungi lebih banyak artifak pemampatan.", "image_prefer_wide_gamut": "Cadangkan warna gamut yang luas", "image_prefer_wide_gamut_setting_description": "Gunakan Paparan P3 untuk lakaran kenit. Ini lebih baik mengekalkan kerancakan imej dengan ruang warna yang luas, tetapi imej mungkin kelihatan berbeza pada peranti lama dengan versi penyemak imbas lama. Imej sRGB disimpan sebagai sRGB untuk mengelakkan peralihan warna.", "image_preview_description": "Imej bersaiz sederhana dengan metadata yang dilucutkan, digunakan semasa melihat aset tunggal dan untuk pembelajaran mesin", @@ -102,7 +105,7 @@ "library_scanning_enable_description": "Dayakan pengimbasan perpustakaan berkala", "library_settings": "Perpustakaan Luaran", "library_settings_description": "Urus tetapan perpustakaan luaran", - "library_tasks_description": "Laksanakan tugas perpustakaan", + "library_tasks_description": "Imbas pustaka luaran untuk aset yang baru dan/atau telah diubah", "library_watching_enable_description": "Perhatikan perpustakaan luaran untuk perubahan fail", "library_watching_settings": "Perhati perpustakaan (EKSPERIMEN)", "library_watching_settings_description": "Perhati fail yang diubah secara automatik", @@ -137,7 +140,7 @@ "machine_learning_smart_search_description": "Cari imej secara semantik menggunakan pembenaman CLIP", "machine_learning_smart_search_enabled": "Dayakan carian pintar", "machine_learning_smart_search_enabled_description": "Jika ditutup, gambar-gambar tidak akan dikodkan untuk carian pintar.", - "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu sehingga satu menjawab dengan jayanya, mengikut urutan dari pertama hingga terakhir.", + "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu mengikut turutan, dari yang pertama hingga yang terakhir, sehingga salah satu memberi maklum balas yang berjaya. Pelayan yang tidak memberi maklum balas akan diabaikan sementara sehingga ia kembali dalam talian.", "manage_concurrency": "Urus Concurrency", "manage_log_settings": "Urus tetapan log", "map_dark_style": "Tema gelap", @@ -146,13 +149,15 @@ "map_gps_settings_description": "Urus Tetapan Peta & GPS (Geokod Terbalik)", "map_implications": "Ciri peta bergantung pada perkhidmatan jubin luaran (tiles.immich.cloud)", "map_light_style": "Tema terang", - "map_manage_reverse_geocoding_settings": "Urus tetapan Geocoding Songsang", + "map_manage_reverse_geocoding_settings": "Urus tetapan Penentuan Alamat Songsang", "map_reverse_geocoding": "Geokoding Sonsang", "map_reverse_geocoding_enable_description": "Dayakan pengekodan geo terbalik", "map_reverse_geocoding_settings": "Tetapan Pengekodan Geo Terbalik", "map_settings": "Peta", "map_settings_description": "Urus tetapan peta", "map_style_description": "URL ke tema peta style.json", + "memory_cleanup_job": "Pembersihan memori", + "memory_generate_job": "Penjanaan memori", "metadata_extraction_job": "Sari metadata", "metadata_extraction_job_description": "Sari maklumat metadata dari setiap aset, seperti GPS, muka-muka, dan pelaraian", "metadata_faces_import_setting": "Dayakan import muka", @@ -161,12 +166,26 @@ "metadata_settings_description": "Urus tetapan metadata", "migration_job": "Migrasi", "migration_job_description": "Pindahkan imej kecil untuk aset-aset dan muka-muka kepada struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Jalankan pengecaman wajah kepada wajah baharu yang dijumpai", + "nightly_tasks_cluster_new_faces_setting": "Kumpulan wajah baharu", + "nightly_tasks_database_cleanup_setting": "Tugasan membersihkan pangkalan data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, luput dari pangkalan data", + "nightly_tasks_generate_memories_setting": "Menjana memori", + "nightly_tasks_generate_memories_setting_description": "Mencipta memori dari aset", + "nightly_tasks_missing_thumbnails_setting": "Menjana lakaran kecil yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Aturan aset tanpa lakaran kecil untuk janaan lakaran kecil", + "nightly_tasks_settings": "Tetapan tugasan malam", + "nightly_tasks_settings_description": "Mengurus tugasan malam", + "nightly_tasks_start_time_setting": "Masa mula", + "nightly_tasks_start_time_setting_description": "Masa di mana pelayan mula bekerja pada tugasan malam", + "nightly_tasks_sync_quota_usage_setting": "Penyelarasan penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Kemaskini kuota simpanan pengguna, berdasarkan kepada penggunaan terkini", "no_paths_added": "Tiada laluan yang ditambah", "no_pattern_added": "Tiada corak ditambah", "note_apply_storage_label_previous_assets": "Nota: Untuk menggunakan Label Storan pada aset yang dimuat naik sebelum ini, jalankan", "note_cannot_be_changed_later": "NOTA: Ini tidak boleh diubah kemudian!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Immich Photo Server \"", + "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Pelayan Gambar Immich \". Pastikan menggunakan alamat yang dibenarkan anda untuk menghantar e-mel.", "notification_email_host_description": "Hos e-mel pelayan (cth. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan ralat-ralat sijil", "notification_email_ignore_certificate_errors_description": "Abaikan ralat pengesahan sijil TLS (tidak disyorkan)", @@ -186,19 +205,24 @@ "oauth_auto_register": "Daftar secara automatik", "oauth_auto_register_description": "Daftar secara automatik pengguna-pengguna baharu selepas mendaftar masuk dengan OAuth", "oauth_button_text": "Teks butang", + "oauth_client_secret_description": "Diperlukan jika PKCE (Proof Key for Code Exchange) tidak disokong oleh penyedia OAuth", "oauth_enable_description": "Log masuk dengan OAuth", "oauth_mobile_redirect_uri": "URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override": "Penggantian URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti ''{callback}''", + "oauth_role_claim": "Tebus peranan", + "oauth_role_claim_description": "Automatik memberi kebenaran pentadbir berdasarkan tuntutan ini. Tuntutan ini mungkin mempunyai sama ada 'pengguna' atau 'pentadbir'.", "oauth_settings": "OAuth", - "oauth_settings_description": "Urus tetapan-tetapan log masuk OAuth", + "oauth_settings_description": "Urus tetapan log masuk OAuth", "oauth_settings_more_details": "Untuk maklumat lanjut tentang ciri ini, rujuk ke dokumen.", "oauth_storage_label_claim": "Tuntutan label storan", "oauth_storage_label_claim_description": "Tetapkan label storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_claim": "Tuntutan kuota storan", "oauth_storage_quota_claim_description": "Tetapkan kuota storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_default": "Kuota storan lalai (GiB)", - "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan apabila tiada tuntutan disediakan (Masukkan 0 untuk kuota tanpa had).", + "oauth_storage_quota_default_description": "Kuota dalam GiB yang akan digunakan jika tiada tuntutan disediakan.", + "oauth_timeout": "Had Masa Permintaan", + "oauth_timeout_description": "Had masa untuk permintaan dalam milisaat", "password_enable_description": "Log masuk dengan e-mel dan kata laluan", "password_settings": "Kata Laluan Log Masuk", "password_settings_description": "Urus tetapan-tetapan kata laluan log masuk", @@ -207,12 +231,12 @@ "quota_size_gib": "Saiz Kuota (GiB)", "refreshing_all_libraries": "Menyegarkan semua perpustakaan", "registration": "Pendaftaran Pentadbir", - "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Admin dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", + "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Pentadbir dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", "require_password_change_on_login": "Perlukan pengguna menukar kata laluan ketika log masuk pertama", "reset_settings_to_default": "Tetapkan semula tetapan kepada lalai", "reset_settings_to_recent_saved": "Tetapkan semula tetapan kepada tetapan yang disimpan baru-baru ini", "scanning_library": "Mengimbas perpustakaan", - "search_jobs": "Cari kerja…", + "search_jobs": "Cari tugasan…", "send_welcome_email": "Hantar e-mel alu-aluan", "server_external_domain_settings": "Domain luaran", "server_external_domain_settings_description": "Domain untuk pautan kongsi awam, termasuk http(s)://", @@ -222,7 +246,7 @@ "server_settings_description": "Urus tetapan pelayan", "server_welcome_message": "Mesej alu-aluan", "server_welcome_message_description": "Mesej yang dipaparkan pada halaman log masuk.", - "sidecar_job": "Metadata kereta sisi", + "sidecar_job": "Metadata sampingan", "sidecar_job_description": "Temui atau segerakkan metadata sampingan daripada sistem fail", "slideshow_duration_description": "Bilangan saat untuk memaparkan setiap imej", "smart_search_job_description": "Jalankan pembelajaran mesin pada aset-aset untuk menyokong carian pintar", @@ -233,9 +257,10 @@ "storage_template_hash_verification_enabled_description": "Mendayakan pengesahan hac, jangan lumpuhkan melainkan anda pasti akan implikasinya", "storage_template_migration": "Penghijrahan templat storan", "storage_template_migration_description": "Gunakan {template} semasa pada aset-aset yang dimuat naik sebelum ini", - "storage_template_migration_info": "Perubahan templat hanya akan digunakan pada aset baharu. Untuk menggunakan templat secara retroaktif pada aset-aset yang dimuat naik sebelum ini, jalankan {job}.", + "storage_template_migration_info": "Templat storan akan menukar semua sambungan fail kepada huruf kecil. Perubahan templat hanya akan digunakan untuk aset baru. Untuk menggunakan templat ini secara retroaktif pada aset yang telah dimuat naik sebelum ini, jalankan {job}.", "storage_template_migration_job": "Kerja Migrasi Templat Storan", "storage_template_more_details": "Untuk butiran lanjut tentang ciri ini, rujuk kepada Templat Storan dan implikasi", + "storage_template_onboarding_description_v2": "Apabila diaktifkan, ciri ini akan mengatur fail secara automatik berdasarkan templat yang ditetapkan oleh pengguna. Untuk maklumat lanjut, sila rujuk dokumentasi.", "storage_template_path_length": "Anggaran kepanjangan laluan: {length, number}/{limit, number}", "storage_template_settings": "Templat Storan", "storage_template_settings_description": "Urus struktur folder dan nama fail aset dimuat naik", @@ -250,7 +275,7 @@ "template_email_update_album": "Templat Kemas kini Album", "template_email_welcome": "Templat e-mel alu-aluan", "template_settings": "Templat Pemberitahuan", - "template_settings_description": "Urus templat tersuai untuk pemberitahuan.", + "template_settings_description": "Urus templat tersuai untuk notifikasi", "theme_custom_css_settings": "CSS tersuai", "theme_custom_css_settings_description": "Lembaran Gaya Lata membolehkan reka bentuk Immich disuaikan.", "theme_settings": "Tetapan Tema", @@ -282,7 +307,7 @@ "transcoding_encoding_options": "Pilihan Pengekodan", "transcoding_encoding_options_description": "Tetapkan codec, resolusi, kualiti dan pilihan lain untuk video yang dikodkan", "transcoding_hardware_acceleration": "Pecutan Perkakasan", - "transcoding_hardware_acceleration_description": "Eksperimen; lebih pantas, tetapi akan mempunyai kualiti yang lebih rendah pada kadar bit yang sama", + "transcoding_hardware_acceleration_description": "Eksperimen: pengekodan semula yang lebih pantas tetapi mungkin mengurangkan kualiti pada kadar bit yang sama", "transcoding_hardware_decoding": "Penyahkodan perkakasan", "transcoding_hardware_decoding_setting_description": "Mendayakan pecutan hujung ke hujung dan bukannya hanya mempercepatkan pengekodan. Mungkin tidak berfungsi pada semua video.", "transcoding_max_b_frames": "Bingkai-B maksimum", @@ -311,8 +336,61 @@ "transcoding_threads_description": "Nilai yang lebih tinggi membawa kepada pengekodan yang lebih pantas, tetapi meninggalkan lebih sedikit ruang untuk pemproses tugas lain semasa aktif. Nilai ini tidak boleh lebih daripada bilangan teras CPU. Memaksimumkan penggunaan jika ditetapkan kepada 0.", "transcoding_tone_mapping": "Pemetaan nada", "transcoding_tone_mapping_description": "Percubaan untuk mengekalkan penampilan video HDR apabila ditukar kepada SDR. Setiap algoritma membuat pertukaran yang berbeza untuk warna, perincian dan kecerahan. Hable mengekalkan perincian, Mobius mengekalkan warna, dan Reinhard mengekalkan kecerahan.", - "transcoding_transcode_policy": "Dasar transkod" + "transcoding_transcode_policy": "Dasar transkod", + "transcoding_transcode_policy_description": "Dasar untuk bila video perlu ditranskod. Video HDR akan sentiasa ditranskod (kecuali jika pengekodan semula dinyahdayakan).", + "transcoding_two_pass_encoding": "Pengekodan dua lelaran", + "transcoding_two_pass_encoding_setting_description": "Transkod dalam dua lelaran untuk menghasilkan video yang ditranskod dengan kualiti lebih baik. Apabila kadar bit maksimum diaktifkan (diperlukan untuk berfungsi dengan H.264 dan HEVC), mod ini akan menggunakan julat kadar bit berdasarkan kadar bit maksimum dan mengabaikan CRF. Untuk VP9, CRF boleh digunakan jika kadar bit maksimum dinyahdayakan.", + "transcoding_video_codec": "Kodek video", + "transcoding_video_codec_description": "VP9 mempunyai kecekapan tinggi dan keserasian web yang baik, tetapi mengambil masa lebih lama untuk ditranskod. HEVC memberikan prestasi yang serupa, tetapi kurang serasi dengan web. H.264 sangat serasi dan pantas untuk ditranskod, tetapi menghasilkan fail yang jauh lebih besar. AV1 ialah kodek paling cekap tetapi tidak disokong pada peranti lama.", + "trash_enabled_description": "Dayakan ciri Tong Sampah", + "trash_number_of_days": "Bilangan hari", + "trash_number_of_days_description": "Bilangan hari untuk menyimpan aset dalam tong sampah sebelum dipadam secara kekal", + "trash_settings": "Tetapan Tong Sampah", + "trash_settings_description": "Urus tetapan tong sampah", + "user_cleanup_job": "Pembersihan pengguna", + "user_delete_delay": "Akaun dan aset {user} akan dijadualkan untuk dipadam secara kekal dalam {delay, plural, one {# hari} other {# hari}}.", + "user_delete_delay_settings": "Kelewatan pemadaman", + "user_delete_delay_settings_description": "Bilangan hari selepas penghapusan sebelum akaun dan aset pengguna dipadam secara kekal. Tugasan pemadaman pengguna dijalankan pada tengah malam untuk menyemak pengguna yang sedia untuk dipadam. Perubahan pada tetapan ini akan dinilai semasa pelaksanaan seterusnya.", + "user_delete_immediately": "Akaun dan aset {user} akan dimasukkan ke dalam baris gilir untuk dipadam secara kekal serta-merta.", + "user_delete_immediately_checkbox": "Masukkan pengguna dan aset ke dalam baris gilir untuk dipadam serta-merta", + "user_details": "Butiran Pengguna", + "user_management": "Pengurusan Pengguna", + "user_password_has_been_reset": "Katalaluan pengguna telah ditetapkan semula:", + "user_password_reset_description": "Sila berikan katalaluan sementara kepada pengguna dan maklumkan bahawa mereka perlu menukar katalaluan semasa log masuk yang seterusnya.", + "user_restore_description": "Akaun {user} akan dipulihkan.", + "user_restore_scheduled_removal": "Pulihkan pengguna – pemadaman dijadualkan pada {date, date, long}", + "user_settings": "Tetapan Pengguna", + "user_settings_description": "Urus tetapan pengguna", + "user_successfully_removed": "Pengguna {email} telah berjaya dipadam.", + "version_check_enabled_description": "Dayakan semakan versi", + "version_check_implications": "Ciri semakan versi bergantung kepada komunikasi berkala dengan github.com", + "version_check_settings": "Semakan Versi", + "version_check_settings_description": "Dayakan/nyahdayakan notifikasi versi baharu", + "video_conversion_job": "Transkod video", + "video_conversion_job_description": "Transkod video untuk keserasian yang lebih luas dengan pelayar dan peranti" }, + "admin_email": "Emel Pentadbir", + "admin_password": "Kata laluan Pentadbir", + "administration": "Pentadbiran", + "advanced": "Lanjutan", + "advanced_settings_beta_timeline_subtitle": "Cuba pengalaman aplikasi baharu", + "advanced_settings_beta_timeline_title": "Garis masa beta", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan pilihan ini untuk menapis media semasa penyegerakan berdasarkan kriteria alternatif. Hanya cuba jika anda menghadapi masalah dengan aplikasi mengesan semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan penapis penyelarasan album peranti alternatif", + "advanced_settings_log_level_title": "Tahap log: {level}", + "advanced_settings_prefer_remote_subtitle": "Sesetengah peranti sangat perlahan untuk memuatkan imej kecil daripada aset lokal. Aktifkan tetapan ini untuk memuatkan imej dari jauh sebagai gantinya.", + "advanced_settings_prefer_remote_title": "Utamakan imej jauh", + "advanced_settings_proxy_headers_subtitle": "Tentukan pengepala proksi yang perlu dihantar oleh Immich dengan setiap permintaan rangkaian", + "advanced_settings_proxy_headers_title": "Pengepala Proksi", + "advanced_settings_self_signed_ssl_subtitle": "Langkau pengesahan sijil SSL untuk titik hujung pelayan. Diperlukan untuk sijil yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Benarkan sijil SSL yang ditandatangani sendiri", + "advanced_settings_sync_remote_deletions_subtitle": "Automatik memadam atau memulihkan satu asset di peranti ini apabila tindakan itu diambil di dalam laman sesawang", + "advanced_settings_sync_remote_deletions_title": "Selaraskan pemadaman kawalan jauh [UJI KAJI]", + "advanced_settings_tile_subtitle": "Tetapan lanjutan pengguna", + "advanced_settings_troubleshooting_subtitle": "Dayakan ciri tambahan untuk menyelesaikan masalah", + "advanced_settings_troubleshooting_title": "Menyelesaikan masalah", + "age_months": "Umur {bulan, plural, satu {# bulan} lain {# bulan}}", + "age_year_months": "Umur 1 tahun, {bulan, plural, satu {# bulan} lain {# bulan}}", "deduplication_criteria_1": "Saiz imej dalam bait", "deduplication_criteria_2": "Kiraan data EXIF", "deduplication_info": "Maklumat Pendeduplikasian", @@ -361,5 +439,6 @@ "year": "Tahun", "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak mempunyai apa-apa pautan yang dikongsi", + "your_wifi_name": "Nama Wi-Fi anda", "zoom_image": "Zum Gambar" } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 3478262019..89b5e298bc 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -22,8 +22,8 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", - "add_tag": "Legg til tag", - "add_to": "Legg til…", + "add_tag": "Legg til tagg", + "add_to": "Legg til i…", "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", @@ -35,7 +35,7 @@ "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For å ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som slutter på \".tif\", bruk \"**/*.tif\". For å ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", "admin_user": "Administrasjonsbruker", - "asset_offline_description": "Denne eksterne bibliotekressursen finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, sjekk tidslinjen din for den tilsvarende ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengelig for Immich og skan biblioteket.", + "asset_offline_description": "Dette eksterne biblioteksobjektet finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende objektet i tidslinjen din. For å gjenopprette objektet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", "authentication_settings_disable_all": "Er du sikker på at du ønsker å deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", @@ -114,7 +114,7 @@ "logging_settings": "Logger", "machine_learning_clip_model": "Clip-modell", "machine_learning_clip_model_description": "Navnet på en CLIP-modell finnes her. Merk at du må kjøre 'Smart Søk'-jobben på nytt for alle bilder etter at du har endret modell.", - "machine_learning_duplicate_detection": "Duplikat-deteksjon", + "machine_learning_duplicate_detection": "Duplikatsøk", "machine_learning_duplicate_detection_enabled": "Aktiver duplikatdeteksjon", "machine_learning_duplicate_detection_enabled_description": "Hvis deaktivert: helt identiske filer vil fremdeles de-duplisert.", "machine_learning_duplicate_detection_setting_description": "Bruk CLIP-embeddings for å finne sannsynlige duplikater", @@ -166,6 +166,20 @@ "metadata_settings_description": "Administrer metadatainnstillinger", "migration_job": "Migrering", "migration_job_description": "Migrer miniatyrbilder for filer og ansikter til den nyeste mappestrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kjør ansiktsgjenkjenning på nylige oppdagede ansikter", + "nightly_tasks_cluster_new_faces_setting": "Grupper nye ansikter", + "nightly_tasks_database_cleanup_setting": "Opprydningsjobber for databasen", + "nightly_tasks_database_cleanup_setting_description": "Rydder opp i gamle, utgåtte data fra databasen", + "nightly_tasks_generate_memories_setting": "Genererer minner", + "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra objekter", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniatyrbilder", + "nightly_tasks_missing_thumbnails_setting_description": "Legg til objekter i kø som mangler miniatyrbilder for generering", + "nightly_tasks_settings": "Innstillinger for nattjobber", + "nightly_tasks_settings_description": "Endre på nattjobber", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tiden som serveren starter med nattjobbene", + "nightly_tasks_sync_quota_usage_setting": "Synkroniser kvotebruk", + "nightly_tasks_sync_quota_usage_setting_description": "Oppdater brukerkvote basert på nåværende bruk", "no_paths_added": "Ingen filstier lagt til", "no_pattern_added": "Ingen mønster lagt til", "note_apply_storage_label_previous_assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobil omdirigerings-URI", "oauth_mobile_redirect_uri_override": "Mobil omdirigerings-URI overstyring", "oauth_mobile_redirect_uri_override_description": "Aktiver når OAuth-leverandøren ikke tillater en mobil URI, som ''{callback}''", + "oauth_role_claim": "Krev Rolle", + "oauth_role_claim_description": "Gi automatisk administratortilgang basert på tilstedeværelsen av dette kravet. Kravet kan ha enten «bruker» eller «administrator».", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", @@ -212,7 +228,7 @@ "password_settings_description": "Administrer innstillinger for passordinnlogging", "paths_validated_successfully": "Alle filstier validert uten problemer", "person_cleanup_job": "Person opprydding", - "quota_size_gib": "Kvotestørrelse (GiB)", + "quota_size_gib": "Kvote (GiB)", "refreshing_all_libraries": "Oppdaterer alle biblioteker", "registration": "Administrator registrering", "registration_description": "Siden du er den første brukeren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgaver. Du vil også opprette eventuelle nye brukere.", @@ -236,12 +252,12 @@ "smart_search_job_description": "Kjør maskinlæring på filer for å støtte smart søk", "storage_template_date_time_description": "Elementets opprettelsestidspunkt brukes for datotid-informasjonen", "storage_template_date_time_sample": "Eksempeltid {date}", - "storage_template_enable_description": "Aktiver lagringstemplatmotoren", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", "storage_template_hash_verification_enabled": "Hash verifisering aktivert", "storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker på konsekvensene", - "storage_template_migration": "Lagringsmal migrering", + "storage_template_migration": "Implementer lagringsmal", "storage_template_migration_description": "Bruk gjeldende {template} på tidligere opplastede bilder", - "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye ressurser. For å anvende malen på tidligere opplastede ressurser, kjør {job}.", + "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye objekter. For å anvende malen på tidligere opplastede objekter, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", "storage_template_onboarding_description_v2": "Når aktivert vil denne funksjonen automatisk organisere filer basert på en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", @@ -250,19 +266,19 @@ "storage_template_settings_description": "Administrer mappestrukturen og filnavnet til opplastede fil", "storage_template_user_label": "{label} er brukerens Lagringsetikett", "system_settings": "Systeminstillinger", - "tag_cleanup_job": "Tag opprydding", + "tag_cleanup_job": "Tagg-opprydding", "template_email_available_tags": "Du kan bruke følgende variabler i din mal: {tags}", "template_email_if_empty": "Hvis malen er tom, vil standard epost bli brut.", "template_email_invite_album": "Inviter Album Mal", "template_email_preview": "Forhåndsvis", - "template_email_settings": "Epost mal", + "template_email_settings": "E-postmaler", "template_email_update_album": "Oppdater Album Mal", - "template_email_welcome": "Mal for velkomst epost", + "template_email_welcome": "Mal for velkomst-e-post", "template_settings": "Varslings Mal", "template_settings_description": "Administrer tilpassede maler for varsling", "theme_custom_css_settings": "Egendefinert CSS", "theme_custom_css_settings_description": "Cascading Style Sheets gjør det mulig å tilpasse designet av Immich.", - "theme_settings": "Tema innstillinger", + "theme_settings": "Tema-innstillinger", "theme_settings_description": "Administrer tilpasning av Immich webgrensesnitt", "thumbnail_generation_job": "Generer miniatyrbilder", "thumbnail_generation_job_description": "Generer store, små og uskarpe miniatyrbilder for hver fil, samt miniatyrbilder for hver person", @@ -357,10 +373,12 @@ "admin_password": "Administrator Passord", "administration": "Administrasjon", "advanced": "Avansert", + "advanced_settings_beta_timeline_subtitle": "Prøv den nye app opplevelsen", + "advanced_settings_beta_timeline_title": "Beta tidslinje", "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for å filtrere mediefiler under synkronisering basert på alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", "advanced_settings_log_level_title": "Loggnivå: {level}", - "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente mikrobilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", + "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente miniatyrbilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest", "advanced_settings_proxy_headers_title": "Proxy headere", @@ -388,6 +406,7 @@ "album_options": "Albumalternativer", "album_remove_user": "Fjerne bruker?", "album_remove_user_confirmation": "Er du sikker på at du vil fjerne {user}?", + "album_search_not_found": "Ingen album ble funnet som traff ditt søk", "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere å dele det med.", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling når et delt album får nye filer", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Standard sorteringsrekkefølge for albumer", "albums_default_sort_order_description": "Standard sorteringsrekkefølge for bilder når man lager et nytt album.", "albums_feature_description": "Samlinger av bilder som kan deles med andre brukere.", + "albums_on_device_count": "Albumer på enheten {count}", "all": "Alle", "all_albums": "Alle album", "all_people": "Alle personer", @@ -426,7 +446,8 @@ "app_bar_signout_dialog_title": "Logg ut", "app_settings": "Appinstillinger", "appears_in": "Vises i", - "archive": "Arkiver", + "archive": "Arkiv", + "archive_action_prompt": "{count} lagt til i arkivet", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", "archive_page_title": "Arkiv ({count})", @@ -464,13 +485,12 @@ "assets": "Filer", "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_added_to_name_count": "Lagt til {count, plural, one {# asset} other {# assets}} i {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", - "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}", + "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", @@ -587,6 +607,7 @@ "cancel": "Avbryt", "cancel_search": "Avbryt søk", "canceled": "Avbrutt", + "canceling": "Avbryter", "cannot_merge_people": "Kan ikke slå sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", @@ -703,7 +724,7 @@ "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", - "darkTheme": "Aktiver mørkt utsende", + "dark_theme": "Aktiver mørk-modus", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", @@ -719,6 +740,7 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", + "delete_action_prompt": "{count} permanen slettet", "delete_album": "Slett album", "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", @@ -732,6 +754,7 @@ "delete_key": "Slett nøkkel", "delete_library": "Slett bibliotek", "delete_link": "Slett lenke", + "delete_local_action_prompt": "{count} slettet lokalt", "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", @@ -745,6 +768,7 @@ "description": "Beskrivelse", "description_input_hint_text": "Legg til beskrivelse ...", "description_input_submit_error": "Feil ved oppdatering av beskrivelse, sjekk loggen for flere detaljer", + "deselect_all": "Avmerk alle", "details": "Detaljer", "direction": "Retning", "disabled": "Deaktivert", @@ -762,6 +786,7 @@ "documentation": "Dokumentasjon", "done": "Ferdig", "download": "Last ned", + "download_action_prompt": "Laster ned {count} objekter", "download_canceled": "Nedlasting avbrutt", "download_complete": "Nedlasting fullført", "download_enqueue": "Nedlasting satt i kø", @@ -799,6 +824,7 @@ "edit_key": "Rediger nøkkel", "edit_link": "Endre lenke", "edit_location": "Endre lokasjon", + "edit_location_action_prompt": "{count} lokasjon endret", "edit_location_dialog_title": "Lokasjon", "edit_name": "Redigere navn", "edit_people": "Rediger personer", @@ -817,6 +843,7 @@ "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker på at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", + "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Skriv inn PINkoden for å aktivere biometrisk autentisering", "enabled": "Aktivert", "end_date": "Slutt dato", @@ -867,7 +894,7 @@ "incorrect_email_or_password": "Feil epost eller passord", "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", - "quota_higher_than_disk_size": "Du har satt en kvote høyere enn diskstørrelsen", + "quota_higher_than_disk_size": "Du har satt kvoten større enn diskstørrelsen", "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", "unable_to_add_comment": "Kan ikke legge til kommentar", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Feilet med å laste fil", "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", + "favorite_action_prompt": "{count} lagt til i favoritter", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", "favorites_page_no_favorites": "Ingen favorittobjekter funnet", @@ -1022,7 +1050,7 @@ "group_year": "Grupper etter år", "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", - "has_quota": "Har kvote", + "has_quota": "Kvote", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Nylig opplastet", "library_page_sort_last_modified": "Sist endret", "library_page_sort_title": "Albumtittel", + "licenses": "Lisenser", "light": "Lys", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", @@ -1173,7 +1202,7 @@ "login_form_save_login": "Forbli innlogget", "login_form_server_empty": "Skriv inn en server-URL.", "login_form_server_error": "Kan ikke koble til server.", - "login_has_been_disabled": "Login har blitt deaktivert.", + "login_has_been_disabled": "Innlogging har blitt deaktivert.", "login_password_changed_error": "Det skjedde en feil ved oppdatering av passordet", "login_password_changed_success": "Passord oppdatert", "logout_all_device_confirmation": "Er du sikker på at du vil logge ut av alle enheter?", @@ -1209,7 +1238,7 @@ "map_settings_dark_mode": "Mørk modus", "map_settings_date_range_option_day": "Siste 24 timer", "map_settings_date_range_option_days": "Siste {days} dager", - "map_settings_date_range_option_year": "Sist år", + "map_settings_date_range_option_year": "Siste år", "map_settings_date_range_option_years": "Siste {years} år", "map_settings_dialog_title": "Kartinnstillinger", "map_settings_include_show_archived": "Inkluder arkiverte", @@ -1227,7 +1256,7 @@ "memories_check_back_tomorrow": "Sjekk igjen i morgen for flere minner", "memories_setting_description": "Administrer hva du ser i minnene dine", "memories_start_over": "Start på nytt", - "memories_swipe_to_close": "Swipe opp for å lukke", + "memories_swipe_to_close": "Sveip opp for å lukke", "memory": "Minne", "memory_lane_title": "Minnefelt {title}", "menu": "Meny", @@ -1235,7 +1264,7 @@ "merge_people": "Slå sammen personer", "merge_people_limit": "Du kan bare slå sammen opp til 5 fjes om gangen", "merge_people_prompt": "Vil du slå sammen disse personene? Denne handlingen kan ikke reverseres.", - "merge_people_successfully": "Personene ble vellykket slått sammen", + "merge_people_successfully": "Sammenslåing av personer var vellykket", "merged_people_count": "Sammenslått {count, plural, one {# person} other {# people}}", "minimize": "Minimer", "minute": "Minutt", @@ -1246,6 +1275,7 @@ "more": "Mer", "move": "Flytt", "move_off_locked_folder": "Flytt ut av låst mappe", + "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", @@ -1258,14 +1288,14 @@ "name": "Navn", "name_or_nickname": "Navn eller kallenavn", "networking_settings": "Nettverk", - "networking_subtitle": "Administrer serverendepunktinnstillingene", + "networking_subtitle": "Administrer serverendepunkt-innstillinger", "never": "aldri", "new_album": "Nytt Album", "new_api_key": "Ny API-nøkkel", "new_password": "Nytt passord", "new_person": "Ny person", - "new_pin_code": "Ny PIN kode", - "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN kode for å sikre tilgangen til denne siden", + "new_pin_code": "Ny PIN-kode", + "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN-kode for å sikre tilgangen til denne siden", "new_user_created": "Ny bruker opprettet", "new_version_available": "NY VERSJON TILGJENGELIG", "newest_first": "Nyeste først", @@ -1282,7 +1312,7 @@ "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for å utforske samlingen din.", - "no_favorites_message": "Legg til favoritter for å raskt finne dine beste bilder og videoer", + "no_favorites_message": "Legg til favoritter for å finne dine beste bilder og videoer raskt", "no_libraries_message": "Opprett et eksternt bibliotek for å se bildene og videoene dine", "no_locked_photos_message": "Bilder og videoer i den låste mappen er skjult og vil ikke vises når du blar i biblioteket.", "no_name": "Ingen navn", @@ -1292,7 +1322,7 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", - "not_in_any_album": "Ikke i noen album", + "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", "notes": "Notater", @@ -1305,7 +1335,7 @@ "notifications": "Notifikasjoner", "notifications_setting_description": "Administrer varsler", "oauth": "OAuth", - "official_immich_resources": "Offisielle Immich Resurser", + "official_immich_resources": "Offisielle Immich-ressurser", "offline": "Frakoblet", "ok": "Ok", "oldest_first": "Eldste først", @@ -1315,7 +1345,7 @@ "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjenester, og kan bli deaktivert når som helst under innstillinger.", "onboarding_server_welcome_description": "La oss sette opp din instans med noen standard innstillinger.", "onboarding_theme_description": "Velg et fargetema for din bruker. Du kan endre denne senere under dine instillinger.", - "onboarding_user_welcome_description": "La oss få deg startet!", + "onboarding_user_welcome_description": "La oss få deg i gang!", "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Støttespiller status", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", + "queue_status": "Kø {count}/{total}", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", "rating_count": "{count, plural, one {# sjerne} other {# stjerner}}", @@ -1474,7 +1505,7 @@ "recent-albums": "Nylige album", "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", - "recently_added_page_title": "Nylig lagt til", + "recently_added_page_title": "Nylig oppført", "recently_taken": "Nylig tatt", "recently_taken_page_title": "Nylig Tatt", "refresh": "Oppdater", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", "remove_from_album": "Fjern fra album", + "remove_from_album_action_prompt": "{count} fjernet fra albumet", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_lock_folder_action_prompt": "{count} fjernet fra låst mappe", "remove_from_locked_folder": "Fjern fra låst mappe", "remove_from_locked_folder_confirmation": "Er du sikker på at du vil flytte disse bildene og videoene ut av den låste mappen? De vil bli synlige i biblioteket.", "remove_from_shared_link": "Fjern fra delt lenke", @@ -1630,7 +1663,7 @@ "server_offline": "Server frakoblet", "server_online": "Server tilkoblet", "server_privacy": "Server personvern", - "server_stats": "Server Statistikk", + "server_stats": "Serverstatistikk", "server_version": "Server Versjon", "set": "Sett", "set_as_album_cover": "Sett som albumomslag", @@ -1667,6 +1700,7 @@ "settings_saved": "Innstillinger lagret", "setup_pin_code": "Sett opp en PINkode", "share": "Del", + "share_action_prompt": "Delte {count} objekter", "share_add_photos": "Legg til bilder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", @@ -1768,6 +1802,7 @@ "sort_title": "Tittel", "source": "Kilde", "stack": "Stable", + "stack_action_prompt": "{count} stakket", "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", @@ -1838,9 +1873,10 @@ "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", + "trash_action_prompt": "{count} flyttet til søppel", "trash_all": "Slett alt", "trash_count": "Slett {count, number}", - "trash_delete_asset": "Slett ressurs", + "trash_delete_asset": "Slett objekt", "trash_emptied": "Søppelbøtte tømt", "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", @@ -1852,16 +1888,18 @@ "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", "type": "Type", - "unable_to_change_pin_code": "Klarte ikke å endre PINkode", + "unable_to_change_pin_code": "Klarte ikke å endre PIN-kode", "unable_to_setup_pin_code": "Klarte ikke å sette opp PINkode", "unarchive": "Fjern fra arkiv", + "unarchive_action_prompt": "{count} slettet fra Arkiv", "unarchived_count": "{count, plural, other {uarkivert #}}", "undo": "Angre", "unfavorite": "Fjern favoritt", + "unfavorite_action_prompt": "{count} slettet fra Favoritter", "unhide_person": "Vis person", "unknown": "Ukjent", "unknown_country": "Ukjent Land", - "unknown_year": "Ukjent År", + "unknown_year": "Ukjent år", "unlimited": "Ubegrenset", "unlink_motion_video": "Koble fra bevegelsesvideo", "unlink_oauth": "Fjern kobling til OAuth", @@ -1873,14 +1911,17 @@ "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", "unselect_all_duplicates": "Fjern markeringen av alle duplikater", - "unselect_all_in": "Fjern alle i {group}", + "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", + "unstack_action_prompt": "{count} ustakket", "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "untagged": "Umerket", "up_next": "Neste", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", "upload_concurrency": "Samtidig opplastning", + "upload_details": "Opplastingsdetaljer", "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", "upload_dialog_title": "Last opp objekt", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", + "users_added_to_album_count": "Lagt til {count, plural, one {#user} other {#users}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", @@ -1919,7 +1961,7 @@ "version": "Versjon", "version_announcement_closing": "Din venn, Alex", "version_announcement_message": "Hei! En ny versjon av Immich er tilgjengelig. Vennligst ta deg tid til å lese utgivelsesnotatene for å sikre at oppsettet ditt er oppdatert for å forhindre feilkonfigurasjoner, spesielt hvis du bruker WatchTower eller en annen mekanisme som håndterer oppdatering av Immich-forekomsten din automatisk.", - "version_history": "Verson Historie", + "version_history": "Versjonshistorikk", "version_history_item": "Installert {version} den {date}", "video": "Video", "video_hover_setting": "Spill av forhåndsvisining mens du holder over musepekeren", @@ -1927,9 +1969,10 @@ "videos": "Videoer", "videos_count": "{count, plural, one {# Video} other {# Videoer}}", "view": "Vis", - "view_album": "Vis Album", + "view_album": "Vis album", "view_all": "Vis alle", "view_all_users": "Vis alle brukere", + "view_details": "Vis detaljer", "view_in_timeline": "Vis i tidslinje", "view_link": "Vis lenke", "view_links": "Vis lenker", @@ -1937,7 +1980,7 @@ "view_next_asset": "Vis neste fil", "view_previous_asset": "Vis forrige fil", "view_qr_code": "Vis QR-kode", - "view_stack": "Vis Stabbel", + "view_stack": "Vis stabel", "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", @@ -1948,12 +1991,12 @@ "week": "Uke", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "Wi-Fi Navn", - "wrong_pin_code": "Feil PINkode", + "wifi_name": "Wi-Fi-navn", + "wrong_pin_code": "Feil PIN-kode", "year": "År", "years_ago": "{years, plural, one {# år} other {# år}} siden", "yes": "Ja", "you_dont_have_any_shared_links": "Du har ingen delte lenker", - "your_wifi_name": "Ditt Wi-Fi navn", + "your_wifi_name": "Ditt Wi-Fi-navn", "zoom_image": "Zoom Bilde" } diff --git a/i18n/nl.json b/i18n/nl.json index 07699e0b31..ecd8418fa5 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -93,7 +93,7 @@ "job_created": "Taak aangemaakt", "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", "job_settings": "Achtergrondtaak-instellingen", - "job_settings_description": "Beheer gelijktijdige taken", + "job_settings_description": "Beheer aantal gelijktijdige taken", "job_status": "Taakstatus", "jobs_delayed": "{jobCount, plural, other {# vertraagd}}", "jobs_failed": "{jobCount, plural, other {# mislukt}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Beheer metadata instellingen", "migration_job": "Migratie", "migration_job_description": "Migreer thumbnails voor assets en gezichten naar de nieuwste mapstructuur", + "nightly_tasks_cluster_faces_setting_description": "Gezichtsherkenning uitvoeren op nieuw gedetecteerde gezichten", + "nightly_tasks_cluster_new_faces_setting": "Cluster nieuwe gezichten", + "nightly_tasks_database_cleanup_setting": "Database opschoon taken", + "nightly_tasks_database_cleanup_setting_description": "Ruim oude data op van de database", + "nightly_tasks_generate_memories_setting": "Genereer herinneringen", + "nightly_tasks_generate_memories_setting_description": "Maak nieuwe herinneringen van assets", + "nightly_tasks_missing_thumbnails_setting": "Genereer ontbrekende thumbnails", + "nightly_tasks_missing_thumbnails_setting_description": "Assets zonder thumbnail in een wachtrij plaatsen voor het genereren van thumbnails", + "nightly_tasks_settings": "Instellingen voor nacht taken", + "nightly_tasks_settings_description": "Beheer nacht taken", + "nightly_tasks_start_time_setting": "Start tijd", + "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nacht taken", + "nightly_tasks_sync_quota_usage_setting": "Synchroniseer quota gebruik", + "nightly_tasks_sync_quota_usage_setting_description": "update gebruiker opslag quota, gebaseerd op huidig gebruik", "no_paths_added": "Geen paden toegevoegd", "no_pattern_added": "Geen patroon toegevoegd", "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Omleidings-URI voor mobiel", "oauth_mobile_redirect_uri_override": "Omleidings-URI voor mobiele app overschrijven", "oauth_mobile_redirect_uri_override_description": "Inschakelen wanneer de OAuth-provider geen mobiele URI toestaat, zoals ''{callback}''", + "oauth_role_claim": "Rol claim", + "oauth_role_claim_description": "Automatisch admin toegang geven als deze claim aanwezig is. De claim kan 'user' of 'admin' zijn.", "oauth_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", @@ -305,7 +321,7 @@ "transcoding_policy_description": "Stel in wanneer een video wordt getranscodeerd", "transcoding_preferred_hardware_device": "Voorkeur hardwareapparaat", "transcoding_preferred_hardware_device_description": "Geldt alleen voor VAAPI en QSV. Stelt de dri node in die wordt gebruikt voor hardwaretranscodering.", - "transcoding_preset_preset": "Preset (-preset)", + "transcoding_preset_preset": "Voorkeuze (-preset)", "transcoding_preset_preset_description": "Compressiesnelheid. Langzamere presets produceren kleinere bestanden en verhogen de kwaliteit bij het targeten van een bepaalde bitrate. VP9 negeert snelheden boven 'faster'.", "transcoding_reference_frames": "Referentie frames", "transcoding_reference_frames_description": "Het aantal frames om naar te verwijzen bij het comprimeren van een bepaald frame. Hogere waarden verbeteren de compressie-efficiëntie, maar vertragen de codering. Bij 0 wordt deze waarde automatisch ingesteld.", @@ -357,10 +373,12 @@ "admin_password": "Beheerder wachtwoord", "administration": "Beheer", "advanced": "Geavanceerd", + "advanced_settings_beta_timeline_subtitle": "Probeer de nieuwe app-ervaring", + "advanced_settings_beta_timeline_title": "Beta tijdlijn", "advanced_settings_enable_alternate_media_filter_subtitle": "Gebruik deze optie om media te filteren tijdens de synchronisatie op basis van alternatieve criteria. Gebruik dit enkel als de app problemen heeft met het detecteren van albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTEEL] Gebruik een alternatieve album synchronisatie filter", "advanced_settings_log_level_title": "Logniveau: {level}", - "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van afbeeldingen die lokaal zijn opgeslagen op het apparaat. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", + "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van lokale afbeeldingen. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", "advanced_settings_proxy_headers_subtitle": "Definieer proxy headers die Immich bij elk netwerkverzoek moet verzenden", "advanced_settings_proxy_headers_title": "Proxy headers", @@ -427,6 +445,7 @@ "app_settings": "App instellingen", "appears_in": "Komt voor in", "archive": "Archief", + "archive_action_prompt": "{count}toegevoegd aan Archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", "archive_page_title": "Archief ({count})", @@ -459,12 +478,11 @@ "asset_skipped_in_trash": "In prullenbak", "asset_uploaded": "Geüpload", "asset_uploading": "Uploaden…", - "asset_viewer_settings_subtitle": "Beheer je instellingen voor gallerijweergave", - "asset_viewer_settings_title": "Foto weergave", + "asset_viewer_settings_subtitle": "Beheer je instellingen voor galerijweergave", + "asset_viewer_settings_title": "Fotoweergave", "assets": "Assets", "assets_added_count": "{count, plural, one {# asset} other {# assets}} toegevoegd", "assets_added_to_album_count": "{count, plural, one {# asset} other {# assets}} aan het album toegevoegd", - "assets_added_to_name_count": "{count, plural, one {# asset} other {# assets}} toegevoegd aan {hasName, select, true {{name}} other {nieuw album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {# asset} other {# assets}} konden niet aan album toegevoegd worden", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", @@ -697,13 +715,13 @@ "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", "current_pin_code": "Huidige PIN code", - "current_server_address": "Huidige serveradres", + "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", - "darkTheme": "Donker thema in-/uitschakelen", + "dark_theme": "Wissel naar donker thema", "date_after": "Datum na", "date_and_time": "Datum en tijd", "date_before": "Datum voor", @@ -719,6 +737,7 @@ "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", + "delete_action_prompt": "{count} permanent verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -732,6 +751,7 @@ "delete_key": "Verwijder key", "delete_library": "Verwijder bibliotheek", "delete_link": "Verwijder link", + "delete_local_action_prompt": "{count} lokaal verwijderd", "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", @@ -762,6 +782,7 @@ "documentation": "Documentatie", "done": "Klaar", "download": "Downloaden", + "download_action_prompt": "{count} assets downloaden", "download_canceled": "Download geannuleerd", "download_complete": "Download voltooid", "download_enqueue": "Download in wachtrij", @@ -799,6 +820,7 @@ "edit_key": "Key bewerken", "edit_link": "Link bewerken", "edit_location": "Locatie bewerken", + "edit_location_action_prompt": "{count} locatie(s) aangepast", "edit_location_dialog_title": "Locatie", "edit_name": "Naam bewerken", "edit_people": "Mensen bewerken", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Kan assets niet laden", "failed_to_load_folder": "Laden van map mislukt", "favorite": "Favoriet", + "favorite_action_prompt": "{count}toegevoegd aan Favorieten", "favorite_or_unfavorite_photo": "Foto markeren als of verwijderen uit favorieten", "favorites": "Favorieten", "favorites_page_no_favorites": "Geen favoriete assets gevonden", @@ -1031,7 +1054,7 @@ "headers_settings_tile_title": "Aangepaste proxy headers", "hi_user": "Hallo {name} ({email})", "hide_all_people": "Verberg alle mensen", - "hide_gallery": "Gallerij verbergen", + "hide_gallery": "Galerij verbergen", "hide_named_person": "Verberg persoon {name}", "hide_password": "Verberg wachtwoord", "hide_person": "Verberg persoon", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Meest recent gemaakt", "library_page_sort_last_modified": "Laatst aangepast", "library_page_sort_title": "Albumtitel", + "licenses": "Licenties", "light": "Licht", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", @@ -1246,6 +1270,7 @@ "more": "Meer", "move": "Verplaats", "move_off_locked_folder": "Verplaats uit vergrendelde map", + "move_to_lock_folder_action_prompt": "{count} toegevoegd aan de vergrendelde map", "move_to_locked_folder": "Verplaats naar vergrendelde map", "move_to_locked_folder_confirmation": "Deze foto’s en video’s worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map", "moved_to_archive": "{count, plural, one {# asset} other {# assets}} verplaatst naar archief", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Aangepast datumbereik verwijderen", "remove_deleted_assets": "Verwijder offline bestanden", "remove_from_album": "Verwijder uit album", + "remove_from_album_action_prompt": "{count} verwijderd uit het album", "remove_from_favorites": "Verwijderen uit favorieten", + "remove_from_lock_folder_action_prompt": "{count} verwijderd uit de vergrendelde map", "remove_from_locked_folder": "Verwijder uit de vergrendelde map", "remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.", "remove_from_shared_link": "Verwijderen uit gedeelde link", @@ -1667,6 +1694,7 @@ "settings_saved": "Instellingen opgeslagen", "setup_pin_code": "Stel een PIN code in", "share": "Delen", + "share_action_prompt": "{count} assets gedeeld", "share_add_photos": "Foto's toevoegen", "share_assets_selected": "{count} geselecteerd", "share_dialog_preparing": "Voorbereiden...", @@ -1768,6 +1796,7 @@ "sort_title": "Titel", "source": "Bron", "stack": "Stapel", + "stack_action_prompt": "{count} gestapeld", "stack_duplicates": "Stapel duplicaten", "stack_select_one_photo": "Selecteer één primaire foto voor de stapel", "stack_selected_photos": "Geselecteerde foto's stapelen", @@ -1838,6 +1867,7 @@ "total": "Totaal", "total_usage": "Totaal gebruik", "trash": "Prullenbak", + "trash_action_prompt": "{count} verwijderd naar de prullenbak", "trash_all": "Verplaats alle naar prullenbak", "trash_count": "{count, number} naar prullenbak", "trash_delete_asset": "Assets naar prullenbak verplaatsen of verwijderen", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", "unarchive": "Herstellen uit archief", + "unarchive_action_prompt": "{count} verwijderd uit het archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", "undo": "Ongedaan maken", "unfavorite": "Verwijderen uit favorieten", + "unfavorite_action_prompt": "{count} verwijderd uit favorieten", "unhide_person": "Persoon zichtbaar maken", "unknown": "Onbekend", "unknown_country": "Onbekend Land", @@ -1875,12 +1907,14 @@ "unselect_all_duplicates": "Deselecteer alle duplicaten", "unselect_all_in": "Deselecteer alles in {group}", "unstack": "Ontstapelen", + "unstack_action_prompt": "{count} ontstapeld", "unstacked_assets_count": "{count, plural, one {# asset} other {# assets}} ontstapeld", + "untagged": "Ongemarkeerd", "up_next": "Volgende", "updated_at": "Geüpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", - "upload_concurrency": "Upload gelijktijdigheid", + "upload_concurrency": "Aantal gelijktijdige uploads", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Bekijk statistieken van accountgebruik", "username": "Gebruikersnaam", "users": "Gebruikers", + "users_added_to_album_count": "{count, plural, one {# Gebruiker} other {# Gebruikers}} toegevoegd aan album", "utilities": "Gereedschap", "validate": "Valideren", "validate_endpoint_error": "Vul een geldige URL in", diff --git a/i18n/nn.json b/i18n/nn.json index 7fb5fdef02..8b04b5d4b2 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -22,18 +22,20 @@ "add_partner": "Legg til partnar", "add_path": "Legg til sti", "add_photos": "Legg til bilete", + "add_tag": "Legg til tagg", "add_to": "Legg til…", - "add_to_album": "Legg til album", + "add_to_album": "Legg til i album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allereie i {album}", - "add_to_shared_album": "Legg til delt album", + "add_to_shared_album": "Legg til i delt album", "add_url": "Legg til URL", - "added_to_archive": "Lagt til arkiv", - "added_to_favorites": "Lagt til favorittar", - "added_to_favorites_count": "Lagt {count, number} til favorittar", + "added_to_archive": "Lagt til i arkiv", + "added_to_favorites": "Lagt til i favorittar", + "added_to_favorites_count": "La til {count, number} i favorittar", "admin": { "add_exclusion_pattern_description": "Legg til utelatingsmønstre. Du kan bruke jokerteikna *, **, og ? for å finne filer som passar mønsteret. For å ignorere alle filer i ei mappe kalla \"Raw\", bruk \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som sluttar på \".tif\", bruk \"**/*.tif\". For å ignorere ein absolutt sti, bruk \"/path/to/ignore/**\".", - "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkurven. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", + "admin_user": "Admin-brukar", + "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkorga. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillingar", "authentication_settings_description": "Handsam passord, OAuth, og godkjenningsinnstillingar", "authentication_settings_disable_all": "Er du sikker at du ynskjer å gjera alle innloggingsmetodar uverksame? Innlogging vil bli heilt uverksam.", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Aktiver tryggingskopiering av database", "backup_keep_last_amount": "Antal tryggingskopiar å behalde", "backup_settings": "Tryggingskopi-innstillingar", - "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", + "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", "cleared_jobs": "Rydda jobbar for: {job}", "config_set_by_file": "Oppsettet blir sett av ei oppsettfil", "confirm_delete_library": "Er du sikker at du vil slette biblioteket {library}?", @@ -51,6 +53,7 @@ "confirm_email_below": "For å bekrefte, skriv \"{email}\" under", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikt på nytt? Det vil òg fjerne namngjevne personar.", "confirm_user_password_reset": "Er du sikker at du vil tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille {user} sin PIN-kode?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Set inn skanningsintervall med cron-formatet. For meir informasjon sjå t.d. Crontab Guru", @@ -66,6 +69,10 @@ "force_delete_user_warning": "ÅTVARING: Handlinga fjernar brukaren og all data. Du kan ikkje angre, og filane kan ikkje gjenopprettast.", "image_format": "Format", "image_format_description": "WebP gjev mindre filstorleik enn JPEG, men er treigare å lage.", + "image_fullsize_description": "Bilete i full storleik utan metadata, i bruk når zooma inn", + "image_fullsize_enabled": "Skru på generering av bilete i full storleik", + "image_fullsize_quality_description": "Kvalitet på bilete i full storleik frå 1-100. Høgare er betre, men gjev større filer.", + "image_fullsize_title": "Innstillingar for bilete i full storleik", "image_prefer_embedded_preview": "Bruk helst innebygd førehandsvisning", "image_prefer_embedded_preview_setting_description": "Når mogleg bruk innebygd førehandsvisning av RAW bilete som inndata til biletehandsaming. For noko bilete kan det gje meir nøyaktige farger, men kvaliteten kjem an på kamera og det kan oppstå komprimeringsartefakt i bilete.", "image_prefer_wide_gamut": "Bruk helst breitt fargespektrum", @@ -121,6 +128,7 @@ "machine_learning_max_detection_distance": "Maksimal oppdagingsverdi", "machine_learning_max_detection_distance_description": "Den største skilnaden mellom to bilete for å rekne dei som duplikat, frå 0.001-0.1. Større verdiar finn fleire duplikat, men kan gje falske treff.", "machine_learning_max_recognition_distance": "Maksimal attkjenningsverdi", + "machine_learning_max_recognition_distance_description": "Maksimal forskjell på to ansikt for å bli rekna som same person, på ein skala frå 0-2. Eit lågare tal kan hindre to personar i å bli rekna som den same, medan eit høgare tal kan hindre at same individ vert merka som to forskjellige personar. Merk at det er lettare å slå saman to personar enn å dele éin person i to, så sikt mot den låge sida av skalaen når mogleg.", "machine_learning_min_detection_score": "Minimum deteksjonsresultat", "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, på ein skala frå 0 til 1. Lågare verdiar vil oppdage fleire ansikt, men kan føre til feilaktige treff.", "machine_learning_min_recognized_faces": "Minimum gjenkjende ansikt", @@ -137,16 +145,38 @@ "map_gps_settings_description": "Administrer innstillingar for kart og GPS (Reversert geokoding)", "map_light_style": "Lys modus", "map_settings": "Kart", + "map_settings_description": "Endre kartinnstillingar", + "map_style_description": "URL til eit style.json-karttema", "metadata_extraction_job": "Hent ut metadata", + "metadata_extraction_job_description": "Hent ut metadata frå kvart bilete, slik som GPS, ansikt og oppløysing", + "metadata_faces_import_setting": "Skru på import av ansikt", + "metadata_faces_import_setting_description": "Importer ansikt frå bilete sine EXIF-data og sidecar-filer", "metadata_settings": "Metadata Innstillinger", + "metadata_settings_description": "Endre metadata-innstillingar", "migration_job": "Migrasjon", "notification_email_from_address": "Frå adresse", + "notification_email_test_email_failed": "Mislukka sending av test-e-post, sjekk konfigurasjonen din", + "notification_email_test_email_sent": "Det vart sendt ei test-melding til {email}. Sjekk e-posten din.", + "notification_email_username_description": "Brukarnamn for autentisering på e-post-serveren", + "notification_enable_email_notifications": "Aktiver e-post-varslingar", "notification_settings": "Varselinnstillingar", + "notification_settings_description": "Endre varslingsinnstillingar, inkludert e-post", "oauth_auto_launch": "Autostart", + "oauth_auto_launch_description": "Start OAuth-innloggingsprosessen automatisk når innloggingssida vert opna", + "oauth_auto_register_description": "Registrer nye brukarar automatisk etter innlogging med OAuth", "oauth_button_text": "Tekst på knapp", + "oauth_client_secret_description": "Krevjast dersom PKCE (Proof Key for Code Exchange) ikkje støttast av OAuth-tilbydaren", + "oauth_enable_description": "Logg inn med OAuth", + "oauth_settings": "OAuth", + "oauth_settings_description": "Innstillingar for innlogging med OAuth", + "oauth_storage_quota_default": "Standard lagringskvote (GiB)", + "oauth_timeout": "Tidsavbrot på førespurnad", + "oauth_timeout_description": "Tidsavbrot for førespurnadar i millisekund", "password_enable_description": "Logg inn med e-post og passord", "password_settings": "Passordinnlogging", + "password_settings_description": "Innstillingar for innlogging med passord", "person_cleanup_job": "Personopprydding", + "quota_size_gib": "Lagringskvote (GiB)", "refreshing_all_libraries": "Laster alle bibliotek opp att", "registration": "Administrator registrering", "registration_description": "Sidan du er den første brukaren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgåver. Du vil òg opprette eventuelle nye brukarar.", @@ -164,8 +194,17 @@ "server_settings_description": "Administrer serverinnstillingar", "server_welcome_message": "Velkomstmelding", "server_welcome_message_description": "Ei melding som synast på innloggingssida.", + "sidecar_job": "Sidecar-metadata", + "sidecar_job_description": "Oppdag eller synkroniser sidecar-metadata frå filsystemet", + "slideshow_duration_description": "Antal sekund å vise kvart bilete", + "storage_template_date_time_sample": "Døme på tid {date}", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", + "storage_template_migration": "Overgang til ny lagringsmal", + "storage_template_migration_job": "Omorganisering etter ny lagringsmal", + "storage_template_settings": "Lagringsmal", "system_settings": "Systeminnstillingar", "template_email_preview": "Førehandsvisning", + "thumbnail_generation_job": "Generer miniatyrbilete", "transcoding_acceleration_nvenc": "NVENC (Krev NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (Krev 7. generasjons Intel CPU eller nyare)", "transcoding_acceleration_rkmpp": "RKMPP (Berre på Rockchip SOCer)", @@ -180,7 +219,11 @@ "transcoding_audio_codec": "Lydkodek", "transcoding_audio_codec_description": "Opus er det valet med høgast lydkvalitet, men mindre kompabilitet med gamlare einingar og programvare.", "transcoding_bitrate_description": "Videoar med bitrate over høgste tillatte verdi, eller i eit format som ikkje er tillate", - "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec." + "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec.", + "transcoding_constant_rate_factor_description": "Videokvalitet. Vanlege verdiar er 23 for H.264, 28 for HEVC, 31 for VP9, og 35 for AV1. Lågare er betre, men gjev større filer.", + "transcoding_hardware_acceleration": "Maskinvare-akselerasjon", + "transcoding_max_bitrate": "Maksimal bitrate", + "transcoding_optimal_description": "Videoar med for høg oppløysing, eller ikkje i eit godkjend format" }, "admin_email": "Adminisrator E-post", "admin_password": "Administratorpassord", diff --git a/i18n/pl.json b/i18n/pl.json index e84c6f53e6..5adc6df943 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -5,7 +5,7 @@ "acknowledge": "Zrozumiałem/łam", "action": "Akcja", "action_common_update": "Aktualizuj", - "actions": "Akcje/i", + "actions": "Akcje", "active": "Aktywne", "activity": "Aktywność", "activity_changed": "Aktywność jest {enabled, select, true {włączona} other {wyłączona}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Zarządzaj ustawieniami metadanych", "migration_job": "Migracja", "migration_job_description": "Przenieś miniatury zasobów i twarzy do najnowszej struktury folderów", + "nightly_tasks_cluster_faces_setting_description": "Uruchom rozpoznawanie twarzy dla nowo wykrytych twarzy", + "nightly_tasks_cluster_new_faces_setting": "Zgrupuj nowe twarze", + "nightly_tasks_database_cleanup_setting": "Zadania związane z czyszczeniem bazy danych", + "nightly_tasks_database_cleanup_setting_description": "Wyczyść stare, nieaktualne dane z bazy danych", + "nightly_tasks_generate_memories_setting": "Generuj wspomnienia", + "nightly_tasks_generate_memories_setting_description": "Stwórz nowe wspomnienia z zasobów", + "nightly_tasks_missing_thumbnails_setting": "Wygeneruj brakujące miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Dodaj zasoby bez miniatur do kolejki generowania miniatur", + "nightly_tasks_settings": "Ustawienia nocnych zadań", + "nightly_tasks_settings_description": "Zarządzaj zadaniami wykonywanymi w nocy", + "nightly_tasks_start_time_setting": "Czas rozpoczęcia", + "nightly_tasks_start_time_setting_description": "Czas, w którym serwer rozpoczyna wykonywanie nocnych zadań", + "nightly_tasks_sync_quota_usage_setting": "Zsynchronizuj wykorzystanie kontyngentu", + "nightly_tasks_sync_quota_usage_setting_description": "Zaktualizuj kontyngent przestrzeni dyskowej użytkownika na podstawie aktualnego zużycia", "no_paths_added": "Nie dodano ścieżki", "no_pattern_added": "Nie dodano wzoru", "note_apply_storage_label_previous_assets": "Uwaga: aby zastosować etykietę magazynu do wcześniej przesłanych zasobów, uruchom", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilny adres zwrotny", "oauth_mobile_redirect_uri_override": "Zapasowy URI przekierowania mobilnego", "oauth_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu ''{callback}''", + "oauth_role_claim": "Oświadczenie roli", + "oauth_role_claim_description": "Automatycznie przyznaj dostęp administratora na podstawie obecności tego oświadczenia. Oświadczenie może mieć wartość „użytkownik” lub „administrator”.", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", @@ -357,10 +373,12 @@ "admin_password": "Hasło Administratora", "administration": "Administracja", "advanced": "Zaawansowane", + "advanced_settings_beta_timeline_subtitle": "Wypróbuj nową funkcjonalność aplikacji", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Użyj tej opcji do filtrowania mediów podczas synchronizacji alternatywnych kryteriów. Używaj tylko wtedy gdy aplikacja ma problemy z wykrywaniem wszystkich albumów.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERYMENTALNE] Użyj alternatywnego filtra synchronizacji albumu", "advanced_settings_log_level_title": "Poziom szczegółowości dziennika: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z zasobów na urządzeniu. Aktywuj to ustawienie, aby ładować zdalne obrazy.", + "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z lokalnych zasobów. Aktywuj to ustawienie, aby ładować zdalne obrazy.", "advanced_settings_prefer_remote_title": "Preferuj obrazy zdalne", "advanced_settings_proxy_headers_subtitle": "Zdefiniuj nagłówki proxy, które Immich powinien wysyłać z każdym żądaniem sieciowym", "advanced_settings_proxy_headers_title": "Nagłówki proxy", @@ -427,6 +445,7 @@ "app_settings": "Ustawienia Aplikacji", "appears_in": "W albumach", "archive": "Archiwum", + "archive_action_prompt": "{count} dodanych do Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasób z archiwum", "archive_page_no_archived_assets": "Nie znaleziono zarchiwizowanych zasobów", "archive_page_title": "Archiwum {count}", @@ -464,7 +483,6 @@ "assets": "Zasoby", "assets_added_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_added_to_album_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do albumu", - "assets_added_to_name_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. Elementów}} nie może być dodana do albumu", "assets_count": "{count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_deleted_permanently": "{count} zostało trwale usuniętych", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", - "darkTheme": "Włącz ciemny motyw", + "dark_theme": "Przełącz ciemny motyw", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", @@ -719,6 +737,7 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", + "delete_action_prompt": "{count} trwale usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", @@ -732,6 +751,7 @@ "delete_key": "Usuń klucz", "delete_library": "Usuń bibliotekę", "delete_link": "Usuń link", + "delete_local_action_prompt": "{count} lokalnie usunięto", "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", "delete_others": "Usuń inne", @@ -799,6 +819,7 @@ "edit_key": "Edytuj klucz", "edit_link": "Edytuj link", "edit_location": "Edytuj lokalizację", + "edit_location_action_prompt": "{count} edytowana lokalizacja", "edit_location_dialog_title": "Lokalizacja", "edit_name": "Edytuj nazwę", "edit_people": "Edytuj osoby", @@ -984,6 +1005,7 @@ "failed_to_load_assets": "Nie udało się załadować zasobów", "failed_to_load_folder": "Nie udało się załadować folderu", "favorite": "Ulubione", + "favorite_action_prompt": "{count} dodane do ulubionych", "favorite_or_unfavorite_photo": "Dodaj lub usuń z ulubionych", "favorites": "Ulubione", "favorites_page_no_favorites": "Nie znaleziono ulubionych zasobów", @@ -1127,6 +1149,7 @@ "library_page_sort_created": "Ostatnio utworzone", "library_page_sort_last_modified": "Ostatnio zmodyfikowany", "library_page_sort_title": "Tytuł albumu", + "licenses": "Licencje", "light": "Jasny", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", @@ -1246,6 +1269,7 @@ "more": "Więcej", "move": "Przenieś", "move_off_locked_folder": "Przenieś z folderu zablokowanego", + "move_to_lock_folder_action_prompt": "{count} dodanych do folderu zablokowanego", "move_to_locked_folder": "Przenieś do folderu zablokowanego", "move_to_locked_folder_confirmation": "Te zdjęcia i filmy zostaną usunięte ze wszystkich albumów i będą widzialne tylko w folderze zablokowanym", "moved_to_archive": "Przeniesiono {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do archiwum", @@ -1495,7 +1519,9 @@ "remove_custom_date_range": "Usuń niestandardowy zakres dat", "remove_deleted_assets": "Usuń Niedostępne Pliki", "remove_from_album": "Usuń z albumu", + "remove_from_album_action_prompt": "{count} usunięto z albumu", "remove_from_favorites": "Usuń z ulubionych", + "remove_from_lock_folder_action_prompt": "{count} usunięte z folderu zablokowanego", "remove_from_locked_folder": "Usuń z folderu zablokowanego", "remove_from_locked_folder_confirmation": "Czy na pewno chcesz przenieść te zdjęcia i filmy z folderu zablokowanego? Będą one widoczne w bibliotece.", "remove_from_shared_link": "Usuń z udostępnionego linku", @@ -1667,6 +1693,7 @@ "settings_saved": "Ustawienia zapisane", "setup_pin_code": "Ustaw kod PIN", "share": "Udostępnij", + "share_action_prompt": "Udostępniono {count} zasobów", "share_add_photos": "Dodaj zdjęcia", "share_assets_selected": "Wybrano {count}", "share_dialog_preparing": "Przygotowywanie…", @@ -1768,6 +1795,7 @@ "sort_title": "Tytuł", "source": "Źródło", "stack": "Stos", + "stack_action_prompt": "{count} zgrupowano", "stack_duplicates": "Stos duplikatów", "stack_select_one_photo": "Wybierz jedno główne zdjęcie do stosu", "stack_selected_photos": "Układaj wybrane zdjęcia", @@ -1838,6 +1866,7 @@ "total": "Całkowity", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", + "trash_action_prompt": "{count} przeniesione do kosza", "trash_all": "Usuń wszystkie", "trash_count": "Kosz {count, number}", "trash_delete_asset": "Kosz/Usuń zasób", @@ -1855,9 +1884,11 @@ "unable_to_change_pin_code": "Nie można zmienić kodu PIN", "unable_to_setup_pin_code": "Nie można ustawić kodu PIN", "unarchive": "Cofnij archiwizację", + "unarchive_action_prompt": "{count} usunięto z archiwum", "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", "undo": "Cofnij", "unfavorite": "Usuń z ulubionych", + "unfavorite_action_prompt": "{count} usunięto z ulubionych", "unhide_person": "Przywróć osobę", "unknown": "Nieznany", "unknown_country": "Nieznane państwo", @@ -1875,7 +1906,9 @@ "unselect_all_duplicates": "Odznacz wszystkie duplikaty", "unselect_all_in": "Odznacz wszystkie w {group}", "unstack": "Rozłóż stos", + "unstack_action_prompt": "{count} odgrupowano", "unstacked_assets_count": "{count, plural, one {Rozłożony # zasób} few {Rozłożone # zasoby} other {Rozłożonych # zasobów}}", + "untagged": "Nieoznaczone", "up_next": "Do następnego", "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", @@ -1912,6 +1945,7 @@ "user_usage_stats_description": "Wyświetl statystyki użytkowania konta", "username": "Nazwa użytkownika", "users": "Użytkownicy", + "users_added_to_album_count": "Dodano {count, plural, one {# użytkownika} other {# użytkowników}} do albumu", "utilities": "Narzędzia", "validate": "Walidacja", "validate_endpoint_error": "Proszę wprowadzić prawidłowy adres URL", diff --git a/i18n/pt.json b/i18n/pt.json index bb88cafbfa..1878c9b9cc 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Gerir definições de metadados", "migration_job": "Migração", "migration_job_description": "Migra miniaturas de ficheiros e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Executar reconhecimento facial em faces detetadas recentemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novas faces", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpar dados antigos e expirados da base de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir de ficheiros", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Colocar em fila ficheiros sem miniaturas para a geração das mesmas", + "nightly_tasks_settings": "Definições de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerir tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora em qual o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento de utilizadores, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", "note_apply_storage_label_previous_assets": "Observação: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não permite um URI móvel, como ''{callback}''", + "oauth_role_claim": "Reivindicação de Funções", + "oauth_role_claim_description": "Automaticamente concede acesso de administrador, com base na presença desta reivindicação. A reivindicação tanto pode ter \"user\" como \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Gerir definições de inicio de sessão do OAuth", "oauth_settings_more_details": "Para mais informações sobre esta funcionalidade, veja a documentação.", @@ -357,10 +373,12 @@ "admin_password": "Palavra-passe do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Experimente as novas funcionalidades da aplicação", + "advanced_settings_beta_timeline_title": "Linha temporal da versão Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilize esta definição para filtrar ficheiros durante a sincronização baseada em critérios alternativos. Utilize apenas se a aplicação estiver com problemas a detetar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar um filtro alternativo de sincronização de álbuns em dispositivos", "advanced_settings_log_level_title": "Nível de registo: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos para carregar miniaturas da memória. Ative esta opção para preferir imagens do servidor.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos a carregar miniaturas da memória interna. Ative esta opção para preferir imagens do servidor.", "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicações com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", @@ -427,6 +445,7 @@ "app_settings": "Definições da Aplicação", "appears_in": "Aparece em", "archive": "Arquivo", + "archive_action_prompt": "{count} adicionados ao Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivo ({count})", @@ -464,7 +483,6 @@ "assets": "Ficheiros", "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao álbum", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "Alternar tema escuro", + "dark_theme": "Alternar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,6 +737,7 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Eliminar", + "delete_action_prompt": "{count} eliminados permanentemente", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", "delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo", @@ -732,6 +751,7 @@ "delete_key": "Eliminar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", @@ -762,6 +782,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Transferir", + "download_action_prompt": "A descarregar {count} ficheiros", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -799,6 +820,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Ocorreu um erro ao carregar ficheiros", "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} adicionados aos favoritos", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhum favorito encontrado", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", @@ -1246,6 +1270,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta trancada", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta trancada", "move_to_locked_folder": "Mover para a pasta trancada", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidas de todos os álbuns, e só serão visíveis na pasta trancada", "moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover ficheiros indisponíveis", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido(s) do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta trancada", "remove_from_locked_folder": "Remover da pasta trancada", "remove_from_locked_folder_confirmation": "Tem a certeza de que quer mover estas fotos e vídeos para fora da pasta trancada? Passarão a ser visíveis na biblioteca.", "remove_from_shared_link": "Remover do link partilhado", @@ -1667,6 +1694,7 @@ "settings_saved": "Definições guardadas", "setup_pin_code": "Configurar um código PIN", "share": "Partilhar", + "share_action_prompt": "Partilhados {count} ficheiros", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionados", "share_dialog_preparing": "Preparando...", @@ -1768,6 +1796,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_action_prompt": "{count} empilhados", "stack_duplicates": "Empilhar itens duplicados", "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", @@ -1838,6 +1867,7 @@ "total": "Total", "total_usage": "Total utilizado", "trash": "Reciclagem", + "trash_action_prompt": "{count} movidos para a reciclagem", "trash_all": "Mover todos para a reciclagem", "trash_count": "Reciclar {count, number}", "trash_delete_asset": "Eliminar ficheiro", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível configurar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} removidos do Arquivo", "unarchived_count": "{count, plural, other {Não arquivado #}}", "undo": "Anular", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removidos dos Favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "Remover seleção de todos os itens duplicados", "unselect_all_in": "Remover seleção de {group}", "unstack": "Desempilhar", + "unstack_action_prompt": "{count} desempilhados", "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização de conta", "username": "Nome de utilizador", "users": "Utilizadores", + "users_added_to_album_count": "{count, plural, one {Foi adicionado # utilizador} other {Foram adicionados # utilizadores}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1951,7 +1986,7 @@ "wifi_name": "Nome da rede Wi-Fi", "wrong_pin_code": "Código PIN errado", "year": "Ano", - "years_ago": "Há {years, plural, one {# ano} other {# anos}} atrás", + "years_ago": "Há {years, plural, one {# ano} other {# anos}}", "yes": "Sim", "you_dont_have_any_shared_links": "Não tem links partilhados", "your_wifi_name": "Nome da sua rede Wi-Fi", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 705180cafd..1af3bda2ac 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -105,7 +105,7 @@ "library_scanning_enable_description": "Habilitar verificação periódica da biblioteca", "library_settings": "Biblioteca Externa", "library_settings_description": "Gerenciar configurações de biblioteca externa", - "library_tasks_description": "Escanear bibliotecas externas para ativos novos ou modificados", + "library_tasks_description": "Verificar se há arquivos novos ou modificados nas bibliotecas externas", "library_watching_enable_description": "Observe bibliotecas externas para alterações de arquivos", "library_watching_settings": "Observação de biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Observe automaticamente os arquivos alterados", @@ -168,7 +168,7 @@ "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", - "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto não pode ser alterado posteriormente!", "notification_email_from_address": "E-mail de origem", "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \". Tenha certeza de ter permissão para enviar e-mails a partir do endereço selecionado.", @@ -196,15 +196,17 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não suportar uma URI de aplicativo, por exemplo ''{callback}''", + "oauth_role_claim": "Declaração de função", + "oauth_role_claim_description": "Dá permissões de administrador baseado no valor desta declaração. A declaração pode conter os valores 'user' ou 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gerenciar configurações de login do OAuth", "oauth_settings_more_details": "Para mais detalhes sobre este recurso, consulte a documentação.", - "oauth_storage_label_claim": "Reivindicação de rótulo de armazenamento", + "oauth_storage_label_claim": "Declaração do rótulo de armazenamento", "oauth_storage_label_claim_description": "Defina automaticamente o rótulo de armazenamento do usuário para o valor desta declaração.", - "oauth_storage_quota_claim": "Cota de armazenamento", + "oauth_storage_quota_claim": "Declaração de cota de armazenamento", "oauth_storage_quota_claim_description": "Defina automaticamente a cota de armazenamento do usuário para o valor desta declaração.", "oauth_storage_quota_default": "Cota de armazenamento padrão (GiB)", - "oauth_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma outra reivindicação for fornecida.", + "oauth_storage_quota_default_description": "Cota em GiB que será usada caso esta declaração não seja fornecida.", "oauth_timeout": "Tempo Limite de Requisição", "oauth_timeout_description": "Tempo limite para requisições, em milissegundos", "password_enable_description": "Login com e-mail e senha", @@ -234,20 +236,20 @@ "sidecar_job_description": "Descubra ou sincronize metadados secundários do sistema de arquivos", "slideshow_duration_description": "Tempo em segundos para exibir cada imagem", "smart_search_job_description": "Execute aprendizado de máquina em arquivos para oferecer suporte à pesquisa inteligente", - "storage_template_date_time_description": "A data e hora da criação do ativo é usado para a informações de data e hora", + "storage_template_date_time_description": "A data e hora da criação do arquivo é usado para a informações de data e hora", "storage_template_date_time_sample": "Exemplo {date}", "storage_template_enable_description": "Habilitar mecanismo de modelo de armazenamento", "storage_template_hash_verification_enabled": "Verificação de hash ativada", "storage_template_hash_verification_enabled_description": "Ativa a verificação de hash, não desative a menos que você tenha certeza das implicações", "storage_template_migration": "Migração de modelo de armazenamento", - "storage_template_migration_description": "Aplique o {template} atual aos arquivos carregados anteriormente", - "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos. Para aplicar retroativamente o modelo aos arquivos carregados anteriormente, execute o {job}.", + "storage_template_migration_description": "Aplicar o {template} atual aos arquivos enviados anteriormente", + "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos; para aplicar o modelo aos arquivos enviados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de Migração de Modelo de Armazenamento", "storage_template_more_details": "Para mais detalhes sobre este recurso, consulte o Modelo de Armazenamento e suas implicações", "storage_template_onboarding_description_v2": "Ao ser ativado, este recurso irá organizar automaticamente os arquivos com base em um modelo definido pelo usuário. Para mais informações, consulte a documentação.", "storage_template_path_length": "Limite aproximado de comprimento do caminho: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Armazenamento", - "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo carregado", + "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo enviado", "storage_template_user_label": "{label} é o Rótulo de Armazenamento do usuário", "system_settings": "Configurações do Sistema", "tag_cleanup_job": "Limpeza de marcadores", @@ -336,7 +338,7 @@ "user_delete_delay_settings": "Remover atraso", "user_delete_delay_settings_description": "Número de dias após a remoção para excluir permanentemente a conta e os arquivos de um usuário. A tarefa de exclusão de usuário é executada à meia-noite para verificar usuários que estão prontos para exclusão. As alterações nesta configuração serão avaliadas na próxima execução.", "user_delete_immediately": "A conta e os arquivos de {user} serão programados para exclusão permanente imediata.", - "user_delete_immediately_checkbox": "Adicionar o usuário e seus ativos na fila para serem deletados imediatamente", + "user_delete_immediately_checkbox": "Adicionar o usuário e seus arquivos na fila para serem deletados imediatamente", "user_details": "Detalhes do Usuário", "user_management": "Gerenciamento de usuários", "user_password_has_been_reset": "A senha do usuário foi redefinida:", @@ -426,7 +428,8 @@ "app_bar_signout_dialog_title": "Sair", "app_settings": "Configurações do Aplicativo", "appears_in": "Aparece em", - "archive": "Arquivados", + "archive": "Arquivar", + "archive_action_prompt": "{count} mídias arquivadas", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivados ({count})", @@ -440,7 +443,7 @@ "asset_action_share_err_offline": "Não foi possível obter os arquivos indisponíveis, ignorando", "asset_added_to_album": "Adicionado ao álbum", "asset_adding_to_album": "Adicionando ao álbum…", - "asset_description_updated": "A descrição do ativo foi atualizada", + "asset_description_updated": "A descrição do arquivo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} não está disponível", "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processando…", @@ -457,14 +460,13 @@ "asset_restored_successfully": "Arquivo restaurado", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na lixeira", - "asset_uploaded": "Carregado", - "asset_uploading": "Carregando…", + "asset_uploaded": "Enviado", + "asset_uploading": "Enviando…", "asset_viewer_settings_subtitle": "Gerenciar as configurações do visualizador da galeria", "asset_viewer_settings_title": "Visualizador de Mídia", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} {hasName, select, true {ao álbum {name}} other {em um novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao álbum", "assets_count": "{count, plural, one {# arquivo} other {# arquivos}}", "assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente", @@ -489,7 +491,7 @@ "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", - "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -502,9 +504,9 @@ "backup_background_service_current_upload_notification": "Enviando {filename}", "backup_background_service_default_notification": "Verificando se há novos arquivos…", "backup_background_service_error_title": "Erro no backup", - "backup_background_service_in_progress_notification": "Fazendo backup de seus ativos…", + "backup_background_service_in_progress_notification": "Fazendo backup de seus arquivos…", "backup_background_service_upload_failure_notification": "Falha ao enviar {filename}", - "backup_controller_page_albums": "Álbuns de backup", + "backup_controller_page_albums": "Backup de álbuns", "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualização da aplicação em segundo plano em Configurações > Geral > Atualização em 2º plano.", "backup_controller_page_background_app_refresh_disabled_title": "Atualização em 2º plano desativada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configurações", @@ -512,40 +514,40 @@ "backup_controller_page_background_battery_info_message": "Para uma melhor experiência de backup em segundo plano, desative todas as otimizações de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso é específico por dispositivo, consulte as informações de como fazer isso com o fabricante do seu dispositivo.", "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Otimizações de bateria", - "backup_controller_page_background_charging": "Apenas durante o carregamento", + "backup_controller_page_background_charging": "Apenas enquanto carrega a bateria", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", "backup_controller_page_background_delay": "Adiar backup de novos arquivos: {duration}", - "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos ativos sem precisar abrir o aplicativo", + "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos arquivos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automático em segundo plano está desativado", "backup_controller_page_background_is_on": "O backup automático em segundo plano está ativado", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", "backup_controller_page_background_wifi": "Apenas no Wi-Fi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado: ", - "backup_controller_page_backup_sub": "Backup de fotos e vídeos", - "backup_controller_page_created": "Criado em: {date}", - "backup_controller_page_desc_backup": "Ative o backup para carregar automaticamente novos ativos no servidor.", - "backup_controller_page_excluded": "Excluído: ", + "backup_controller_page_backup_selected": "Selecionados: ", + "backup_controller_page_backup_sub": "Total de mídias com backup", + "backup_controller_page_created": "Data: {date}", + "backup_controller_page_desc_backup": "Ative para fazer backup automático dos novos arquivos ao abrir este aplicativo.", + "backup_controller_page_excluded": "Ignorados: ", "backup_controller_page_failed": "Falhou ({count})", - "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", + "backup_controller_page_filename": "Arquivo: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", - "backup_controller_page_info": "Informações de backup", - "backup_controller_page_none_selected": "Nenhum selecionado", + "backup_controller_page_info": "Informações do backup", + "backup_controller_page_none_selected": "Nenhum álbum selecionado", "backup_controller_page_remainder": "Restante", - "backup_controller_page_remainder_sub": "Fotos e vídeos restantes para fazer backup da seleção", + "backup_controller_page_remainder_sub": "Mídias nos álbuns selecionados que ainda não tem backup", "backup_controller_page_server_storage": "Armazenamento do servidor", - "backup_controller_page_start_backup": "Iniciar backup", - "backup_controller_page_status_off": "O backup está desativado", - "backup_controller_page_status_on": "O backup está ativado", + "backup_controller_page_start_backup": "Iniciar backup manual", + "backup_controller_page_status_off": "O backup automático está desativado", + "backup_controller_page_status_on": "O backup automático está ativado", "backup_controller_page_storage_format": "{used} de {total} usados", - "backup_controller_page_to_backup": "Álbuns para backup", - "backup_controller_page_total_sub": "Todas as fotos e vídeos únicos dos álbuns selecionados", - "backup_controller_page_turn_off": "Desativar o backup", - "backup_controller_page_turn_on": "Ativar Backup", - "backup_controller_page_uploading_file_info": "Carregando informações do arquivo", + "backup_controller_page_to_backup": "Escolha os álbuns para fazer backup", + "backup_controller_page_total_sub": "Total de mídias nos álbuns selecionados", + "backup_controller_page_turn_off": "Desativar backup automático", + "backup_controller_page_turn_on": "Ativar backup automático", + "backup_controller_page_uploading_file_info": "Informações do arquivo", "backup_err_only_album": "Não é possível remover o único álbum", - "backup_info_card_assets": "ativos", + "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", @@ -570,7 +572,7 @@ "cache_settings_clear_cache_button": "Limpar o cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que são bloqueados pelo app", + "cache_settings_duplicated_assets_subtitle": "Mídias ignoradas pelo app", "cache_settings_duplicated_assets_title": "Arquivos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -658,9 +660,9 @@ "control_bottom_app_bar_create_new_album": "Criar novo álbum", "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", - "control_bottom_app_bar_edit_location": "Editar Localização", + "control_bottom_app_bar_edit_location": "Alterar Local", "control_bottom_app_bar_edit_time": "Editar data e hora", - "control_bottom_app_bar_share_link": "Compartilhar Link", + "control_bottom_app_bar_share_link": "Link", "control_bottom_app_bar_share_to": "Compartilhar", "control_bottom_app_bar_trash_from_immich": "Mover para a Lixeira", "copied_image_to_clipboard": "Imagem copiada para a área de transferência.", @@ -680,7 +682,7 @@ "create_album_page_untitled": "Sem título", "create_library": "Criar biblioteca", "create_link": "Criar link", - "create_link_to_share": "Criar link para partilhar", + "create_link_to_share": "Criar link e compartilhar", "create_link_to_share_description": "Permitir que qualquer pessoa com o link veja a(s) foto(s) selecionada(s)", "create_new": "CRIAR NOVO", "create_new_person": "Criar nova pessoa", @@ -703,7 +705,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "trocar para tema escuro", + "dark_theme": "Usar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,6 +721,7 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", + "delete_action_prompt": "{count} deletados permanentemente", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serão excluídos permanentemente do Immich e do seu dispositivo", @@ -781,7 +784,7 @@ "downloading": "Baixando", "downloading_asset_filename": "Baixando arquivo {filename}", "downloading_media": "Baixando mídia", - "drop_files_to_upload": "Solte arquivos em qualquer lugar para carregar", + "drop_files_to_upload": "Solte os arquivos em qualquer lugar para enviar", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, são duplicados", "duration": "Duração", @@ -799,6 +802,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -814,7 +818,7 @@ "email": "E-mail", "email_notifications": "Notificações por e-mail", "empty_folder": "A pasta está vazia", - "empty_trash": "Esvaziar lixo", + "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá permanentemente do Immich todos os arquivos que estão na lixeira.\nVocê não pode desfazer esta ação!", "enable": "Habilitar", "enable_biometric_auth_description": "Insira seu código PIN para ativar a autenticação por biometria", @@ -855,8 +859,8 @@ "failed_to_edit_shared_link": "Falha ao editar o link compartilhado", "failed_to_get_people": "Falha na obtenção de pessoas", "failed_to_keep_this_delete_others": "Falha ao manter este arquivo e excluir os outros", - "failed_to_load_asset": "Não foi possível carregar o ativo", - "failed_to_load_assets": "Não foi possível carregar os ativos", + "failed_to_load_asset": "Não foi possível carregar o arquivo", + "failed_to_load_assets": "Não foi possível carregar os arquivos", "failed_to_load_notifications": "Falha ao carregar notificações", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover a chave do produto", @@ -949,7 +953,7 @@ "unable_to_update_settings": "Não foi possível atualizar as configurações", "unable_to_update_timeline_display_status": "Não foi possível atualizar o modo de visualização da linha do tempo", "unable_to_update_user": "Não foi possível atualizar o usuário", - "unable_to_upload_file": "Não foi possível carregar o arquivo" + "unable_to_upload_file": "Não foi possível enviar o arquivo" }, "exif": "Exif", "exif_bottom_sheet_description": "Adicionar descrição...", @@ -977,13 +981,14 @@ "external": "Externo", "external_libraries": "Bibliotecas externas", "external_network": "Rede externa", - "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo", + "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando o primeiro endereço abaixo que obtiver sucesso, começando do topo da lista para baixo", "face_unassigned": "Sem nome", "failed": "Falhou", "failed_to_authenticate": "Não foi possível autenticar", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} marcados como favorito", "favorite_or_unfavorite_photo": "Marque ou desmarque a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhuma mídia favorita encontrada", @@ -1138,7 +1143,7 @@ "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", "local_asset_cast_failed": "Não é possível transmitir um arquivo que não foi enviado ao servidor", "local_network": "Rede local", - "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", + "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", "location_permission_content": "Para utilizar a função de troca automática de URL é necessário a permissão de localização precisa, para que seja possível ler o nome da rede Wi-Fi", "location_picker_choose_on_map": "Escolha no mapa", @@ -1147,7 +1152,7 @@ "location_picker_longitude_error": "Digite uma longitude válida", "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", - "locked_folder": "Pasta Trancada", + "locked_folder": "Pasta com senha", "log_out": "Sair", "log_out_all_devices": "Sair de todos dispositivos", "logged_in_as": "Usuário atual: {user}", @@ -1246,6 +1251,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta com senha", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta com senha", "move_to_locked_folder": "Mover para a pasta com senha", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidos de todos os álbuns e somente poderão ser visualizados de dentro da pasta com senha", "moved_to_archive": "{count, plural, one {# mídia foi arquivada} other {# mídias foram arquivadas}}", @@ -1276,12 +1282,12 @@ "no_albums_with_name_yet": "Parece que você ainda não tem nenhum álbum com esse nome.", "no_albums_yet": "Parece que você ainda não tem nenhum álbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualização de fotos", - "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", + "no_assets_message": "CLIQUE PARA ENVIAR SUA PRIMEIRA FOTO", "no_assets_to_show": "Não há arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo encontrado", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", "no_exif_info_available": "Sem informações exif disponíveis", - "no_explore_results_message": "Carregue mais fotos para explorar sua coleção.", + "no_explore_results_message": "Envie mais fotos para explorar sua coleção.", "no_favorites_message": "Adicione aos favoritos para encontrar suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver suas fotos e vídeos", "no_locked_photos_message": "Fotos e vídeos na pasta com senha são ocultos e não serão exibidos enquanto explora ou pesquisa na biblioteca.", @@ -1294,7 +1300,7 @@ "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", "not_in_any_album": "Fora de álbum", "not_selected": "Não selecionado", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "notes": "Notas", "nothing_here_yet": "Ainda não existe nada aqui", "notification_permission_dialog_content": "Para ativar as notificações, vá em Configurações e selecione permitir.", @@ -1369,7 +1375,7 @@ "permanent_deletion_warning_setting_description": "Exibe um aviso ao deletar arquivos de forma permanente", "permanently_delete": "Deletar permanentemente", "permanently_delete_assets_count": "Excluir permanentemente {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este ativo?} other {estes # ativos?}} Esta ação também removerá {count, plural, one {o ativo} other {os ativos}} de um ou mais álbuns.", + "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este arquivo?} other {estes # arquivos?}} Esta ação também removerá {count, plural, one {o arquivo} other {os arquivos}} de um ou mais álbuns.", "permanently_deleted_asset": "Arquivo deletado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo permanentemente excluído} other {# arquivos permanentemente excluídos}}", "permission": "Permissão", @@ -1495,7 +1501,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover arquivos excluídos", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta com senha", "remove_from_locked_folder": "Remover da pasta com senha", "remove_from_locked_folder_confirmation": "Tem a certeza de que deseja mover estes arquivos para fora da pasta com senha? Eles ficarão visíveis na biblioteca principal.", "remove_from_shared_link": "Remover do link compartilhado", @@ -1531,7 +1539,7 @@ "restore_user": "Restaurar usuário", "restored_asset": "Arquivo restaurado", "resume": "Continuar", - "retry_upload": "Tentar carregar novamente", + "retry_upload": "Tentar enviar novamente", "review_duplicates": "Revisar duplicidade", "role": "Função", "role_editor": "Editor", @@ -1626,7 +1634,7 @@ "send_welcome_email": "Enviar E-mail de boas vindas", "server_endpoint": "URL do servidor", "server_info_box_app_version": "Versão do aplicativo", - "server_info_box_server_url": "URL do servidor", + "server_info_box_server_url": "Endereço", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", "server_privacy": "Privacidade do servidor", @@ -1713,7 +1721,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link compartilhado", - "shared_links": "Links compartilhados", + "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", "shared_with_me": "Compartilhado comigo", @@ -1838,6 +1846,7 @@ "total": "Total", "total_usage": "Utilização total", "trash": "Lixeira", + "trash_action_prompt": "{count} enviados à lixeira", "trash_all": "Mover todos para o lixo", "trash_count": "Lixo {count, number}", "trash_delete_asset": "Jogar na lixeira/Excluir Arquivo", @@ -1855,9 +1864,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível criar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} desarquivado", "unarchived_count": "{count, plural, one {# Desarquivado} other {# Desarquivados}}", "undo": "Desfazer", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removido dos favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1876,20 +1887,21 @@ "unselect_all_in": "Remover seleção de {group}", "unstack": "Retirar do grupo", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", - "upload": "Carregar", + "upload": "Enviar", "upload_concurrency": "Envios simultâneos", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", - "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos carregados.", + "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos.", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", - "upload_status_uploaded": "Carregado", - "upload_success": "Carregado com sucesso, atualize a página para ver os novos arquivos.", + "upload_status_uploaded": "Enviado", + "upload_success": "Enviado com sucesso, atualize a página para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", "url": "URL", diff --git a/i18n/ro.json b/i18n/ro.json index 0b2ef61a36..46197e943f 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -14,6 +14,7 @@ "add_a_location": "Adaugă o locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", + "add_endpoint": "Adaugă punct final", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", "add_location": "Adaugă locație", @@ -21,6 +22,7 @@ "add_partner": "Adaugă partener", "add_path": "Adaugă o cale", "add_photos": "Adaugă fotografii", + "add_tag": "Adaugă etichetă", "add_to": "Adaugă la…", "add_to_album": "Adaugă în album", "add_to_album_bottom_sheet_added": "Adăugat în {album}", @@ -32,6 +34,7 @@ "added_to_favorites_count": "Adăugat {count, number} la favorite", "admin": { "add_exclusion_pattern_description": "Adăugați modele de excludere. Globing folosind *, ** și ? este suportat. Pentru a ignora toate fișierele din orice director numit „Raw”, utilizați „**/Raw/**”. Pentru a ignora toate fișierele care se termină în „.tif”, utilizați „**/*.tif”. Pentru a ignora o cale absolută, utilizați „/path/to/ignore/**”.", + "admin_user": "Utilizator admin", "asset_offline_description": "Acest material din biblioteca externă nu se mai găsește pe disc și a fost mutat în coșul de gunoi. Dacă fișierul a fost mutat în bibliotecă, verificați cronologia pentru noul material corespunzător. Pentru a restabili acest material, asigurați-vă că calea fișierului de mai jos poate fi accesată de Immich și scanați biblioteca.", "authentication_settings": "Setări de Autentificare", "authentication_settings_description": "Gestionează parola, OAuth și alte setări de autentificare", @@ -39,8 +42,8 @@ "authentication_settings_reenable": "Pentru a reactiva, folosește Comandă Server.", "background_task_job": "Activități de Fundal", "backup_database": "Salvare Bază de Date", - "backup_database_enable_description": "Activare salvare bază de date", - "backup_keep_last_amount": "Cantitatea de copii de rezervă anterioare de păstrat", + "backup_database_enable_description": "Activare salvarea bazei de date", + "backup_keep_last_amount": "Număr de copii de rezervă anterioare de păstrat", "backup_settings": "Setări Copii de Rezervă", "backup_settings_description": "Gestionați setările de salvare a bazei de date", "cleared_jobs": "Activități eliminate pentru: {job}", @@ -50,6 +53,7 @@ "confirm_email_below": "Pentru a confirma, tastați „{email}” mai jos", "confirm_reprocess_all_faces": "Sigur doriți să reprocesați toate fețele? Acest lucru va șterge și persoanele cu nume.", "confirm_user_password_reset": "Sigur doriți să resetați parola utilizatorului {user}?", + "confirm_user_pin_code_reset": "Ești sigur că vrei să resetezi codul PIN al {user}?", "create_job": "Creează sarcină", "cron_expression": "Expresia cron", "cron_expression_description": "Setați intervalul de scanare folosind formatul cron. Pentru mai multe informații, consultați de ex. Crontab Guru", @@ -167,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele încărcate anterior, executați", "note_cannot_be_changed_later": "NOTĂ: Nu se va mai putea modifica ulterior!", "notification_email_from_address": "De la adresa", - "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”", + "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”. Asigură-te că folosești o adresă de la care ai permisiunea de a trimite e-mailuri.", "notification_email_host_description": "Adresa serverului de email (ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ingnoră erorile de certificat", "notification_email_ignore_certificate_errors_description": "Ignoră erorile de validare a certificatului TLS (nerecomandat)", @@ -187,6 +191,7 @@ "oauth_auto_register": "Auto înregistrare", "oauth_auto_register_description": "Înregistrează automat utilizatori noi după autentificarea cu OAuth", "oauth_button_text": "Text buton", + "oauth_client_secret_description": "Necesar dacă PKCE (Proof Key for Code Exchange) nu este suportat de furnizorul OAuth", "oauth_enable_description": "Autentifică-te cu OAuth", "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", @@ -199,7 +204,9 @@ "oauth_storage_quota_claim": "Revendicare spațiu de stocare", "oauth_storage_quota_claim_description": "Setează automat spațiul de stocare al utilizatorului la valoarea acestei cereri.", "oauth_storage_quota_default": "Cota implicită a spațiului de stocare (GiB)", - "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare (introduceți 0 pentru spațiu nelimitat).", + "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare.", + "oauth_timeout": "Solicitarea a expirat", + "oauth_timeout_description": "Timp de expirare pentru solicitări în milisecunde", "password_enable_description": "Autentificare cu email și parolǎ", "password_settings": "Autentificare cu Parolǎ", "password_settings_description": "Gestioneazǎ setǎrile de autentificare cu parola", @@ -237,6 +244,7 @@ "storage_template_migration_info": "Șablonul de stocare va converti extensiile in litere mici. Modificările șablonului se vor aplica doar materialelor noi. Pentru a aplica retroactiv șablonul la materialele încărcate anterior, rulați {job}.", "storage_template_migration_job": "Sarcină Migrare Șablon Stocare", "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", + "storage_template_onboarding_description_v2": "Când este activată, această funcție va organiza automat fișierele pe baza șablonului definit de către utilizator. Pentru mai multe informații, accesează documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", "storage_template_settings": "Șablon Stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele încărcate", @@ -251,7 +259,7 @@ "template_email_update_album": "Actualizați Șablonul de Album", "template_email_welcome": "Șablon de e-mail de bun venit", "template_settings": "Șabloane de Notificare", - "template_settings_description": "Gestionați șabloanele personalizate pentru notificări.", + "template_settings_description": "Gestionați șabloanele personalizate pentru notificări", "theme_custom_css_settings": "CSS personalizat", "theme_custom_css_settings_description": "Foile de stil în cascadă (CSS) permit personalizarea designului Immich.", "theme_settings": "Setări Temă", @@ -283,7 +291,7 @@ "transcoding_encoding_options": "Opțiuni codificare", "transcoding_encoding_options_description": "Setează codecuri , calitatea, rezoluția și alte opțiuni pentru videoclipuri codificare", "transcoding_hardware_acceleration": "Accelerare Hardware", - "transcoding_hardware_acceleration_description": "Experimental; mult mai rapid, dar va avea o calitate mai scăzută la același bitrate", + "transcoding_hardware_acceleration_description": "Experimental: transcodare mai rapidă, dar poate reduce calitatea la aceeași rată de biți", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă în loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", "transcoding_max_b_frames": "Număr maxim de cadre B", @@ -329,6 +337,7 @@ "user_delete_delay_settings_description": "Numărul de zile după eliminare până la ștergerea permanentă a contului și a resurselor unui utilizator. Procesul de ștergere a utilizatorului rulează la miezul nopții pentru a verifica utilizatorii care sunt pregătiți pentru ștergere. Modificările aduse acestei setări vor fi evaluate la următoarea execuție.", "user_delete_immediately": "Contul și resursele utilizatorului {user} vor fi puse în coadă pentru ștergere permanentă imediat.", "user_delete_immediately_checkbox": "Pune utilizatorul și resursele în coadă pentru ștergere imediată", + "user_details": "Detalii utilizator", "user_management": "Gestionarea Utilizatorilor", "user_password_has_been_reset": "Parola utilizatorului a fost resetată:", "user_password_reset_description": "Vă rugăm să furnizați utilizatorului parola temporară și să îi informați că va trebui să o schimbe la următoarea autentificare.", @@ -353,6 +362,8 @@ "advanced_settings_log_level_title": "Nivel log: {level}", "advanced_settings_prefer_remote_subtitle": "Unele dispozitive întâmpină dificultăți în încărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a încărca imaginile de la distanță în schimb.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", + "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", + "advanced_settings_proxy_headers_title": "Antete Proxy", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", "advanced_settings_sync_remote_deletions_subtitle": "Ștergeți sau restaurați automat un element de pe acest dispozitiv atunci când acțiunea este efectuată pe web", @@ -382,6 +393,7 @@ "album_updated_setting_description": "Primiți o notificare prin e-mail când un album partajat are elemente noi", "album_user_left": "A părăsit {album}", "album_user_removed": "{user} eliminat", + "album_viewer_appbar_delete_confirm": "Ești sigur că vrei să ștergi acest album din contul tău?", "album_viewer_appbar_share_err_delete": "Ștergere album eșuată", "album_viewer_appbar_share_err_leave": "Părăsire album eșuată", "album_viewer_appbar_share_err_remove": "Probleme la ștergerea resurselor din album", @@ -392,6 +404,9 @@ "album_with_link_access": "Permite oricui cu link-ul să vadă fotografiile și persoanele din acest album.", "albums": "Albume", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albume}}", + "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", + "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", + "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -412,11 +427,13 @@ "app_settings": "Setări Aplicație", "appears_in": "Apare în", "archive": "Arhivă", + "archive_action_prompt": "{count} adăugate la Arhivă", "archive_or_unarchive_photo": "Arhiveazǎ sau dezarhiveazǎ fotografia", "archive_page_no_archived_assets": "Nu au fost găsite resurse favorite", "archive_page_title": "Arhivă ({count})", "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (în GiB)", + "archived": "Arhivat", "archived_count": "{count, plural, other {Arhivat/e#}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", @@ -427,33 +444,52 @@ "asset_description_updated": "Descrierea resursei a fost actualizată", "asset_filename_is_offline": "Resursa {filename} este offline", "asset_has_unassigned_faces": "Resursa are fețe neatribuite", + "asset_hashing": "Calculare amprentă digitală", + "asset_list_group_by_sub_title": "Grupare după", "asset_list_layout_settings_dynamic_layout_title": "Aspect dinamic", "asset_list_layout_settings_group_automatically": "Automat", "asset_list_layout_settings_group_by": "Grupează resurse după", "asset_list_layout_settings_group_by_month_day": "Lună + zi", + "asset_list_layout_sub_title": "Aspect", "asset_list_settings_subtitle": "Setări format grilă fotografii", "asset_list_settings_title": "Grilă fotografii", "asset_offline": "Resursă Offline", "asset_offline_description": "Această resursă externă nu mai este găsită pe disc. Contactează te rog administratorul tău Immich pentru ajutor.", + "asset_restored_successfully": "Date restaurate cu succes", "asset_skipped": "Sărit", "asset_skipped_in_trash": "În coșul de gunoi", "asset_uploaded": "Încărcat", "asset_uploading": "Se incarcă…", + "asset_viewer_settings_subtitle": "Gestionați setările de vizualizare a galeriei", + "asset_viewer_settings_title": "Vizualizator resurse", "assets": "Resurse", "assets_added_count": "Adăugat {count, plural, one {# resursă} other {# resurse}}", "assets_added_to_album_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în album", - "assets_added_to_name_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în {hasName, select, true {{name}} other {albumul nou}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} nu pot fi adăugate în album", "assets_count": "{count, plural, one {# resursă} other {# resurse}}", + "assets_deleted_permanently": "{count} poză/poze ștearsă/șterse permanent", + "assets_deleted_permanently_from_server": "{count} poză/poze ștearsă/șterse permanent din serverul Immich", + "assets_downloaded_failed": "{count, plural, one {S-a descărcat # fișier – {error} fișier eșuat} other {S-au descărcat # fișiere – {error} fișiere eșuate}}", + "assets_downloaded_successfully": "{count, plural, one {S-a descărcat cu succes # fișier} other {S-au descărcat cu succes # fișiere}}", "assets_moved_to_trash_count": "Am mutat {count, plural, one {# resursă} other {# resurse}} în coșul de gunoi", "assets_permanently_deleted_count": "Șters permanent {count, plural, one {# resursă} other {# resurse}}", "assets_removed_count": "Eliminat {count, plural, one {# resursă} other {# resurse}}", + "assets_removed_permanently_from_device": "{count} resursă(e) eliminate permanent din dispozitivul dvs.", "assets_restore_confirmation": "Ești sigur că vrei să restaurezi toate resursele tale din coșul de gunoi? Nu poți anula această acțiune! Ține minte că resursele offline nu se restaurează astfel.", "assets_restored_count": "Restaurat {count, plural, one {# resursă} other {# resurse}}", + "assets_restored_successfully": "{count} resursă(e) restaurate cu succes", + "assets_trashed": "{count} resursă(e) eliminate", "assets_trashed_count": "Mutat în coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", + "assets_trashed_from_server": "{count} resursă(e) eliminate de pe serverul Immich", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", "authorized_devices": "Dispozitive Autorizate", + "automatic_endpoint_switching_subtitle": "Conectează-te local prin rețeaua Wi‐Fi configurată când este valabilă și prin rețele alternative în caz contrar", + "automatic_endpoint_switching_title": "Alternare URL automată", + "autoplay_slideshow": "Derulare slideshow automat", "back": "Înapoi", "back_close_deselect": "Înapoi, închidere sau deselectare", + "background_location_permission": "Permisiune locație în fundal", + "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi împrăștiate în mai multe albume. Prin urmare, albumele pot fi incluse sau excluse în timpul procesului de backup.", @@ -474,6 +510,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Mergi la setări", "backup_controller_page_background_battery_info_link": "Arată-mi cum", "backup_controller_page_background_battery_info_message": "Pentru cea mai bună experiență a backup-ului în fundal, te rugăm să dezactivezi orice optimizare pentru baterie care restricționează activitatea în fundal pentru Immich.\n\nDeoarece aceasta este specifică fiecărui dispozitiv, te rugăm verifică informațiile necesare tipului tău de dispozitiv.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizări baterie", "backup_controller_page_background_charging": "Doar în timpul încărcării", "backup_controller_page_background_configure_error": "Configurare serviciu în fundal eșuată", @@ -483,7 +520,8 @@ "backup_controller_page_background_is_on": "Backup-ul automat în fundal este activat", "backup_controller_page_background_turn_off": "Dezactivează serviciul în fundal", "backup_controller_page_background_turn_on": "Activează serviciul în fundal", - "backup_controller_page_background_wifi": "Doar conectat la WiFi", + "backup_controller_page_background_wifi": "Numai prin Wi-Fi", + "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selectat(e): ", "backup_controller_page_backup_sub": "S-a făcut backup pentru fotografii și videoclipuri", "backup_controller_page_created": "Creat la: {date}", @@ -491,6 +529,7 @@ "backup_controller_page_excluded": "Exclus(e): ", "backup_controller_page_failed": "Eșuate ({count})", "backup_controller_page_filename": "Nume fișier: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informații backup", "backup_controller_page_none_selected": "Nici o selecție", "backup_controller_page_remainder": "Rămas(e)", @@ -504,14 +543,20 @@ "backup_controller_page_total_sub": "Toate fotografiile și videoclipurile unice din albumele selectate", "backup_controller_page_turn_off": "Dezactivează backup-ul în prim-plan", "backup_controller_page_turn_on": "Activează backup-ul în prim-plan", - "backup_controller_page_uploading_file_info": "Încărcare informații fișier", + "backup_controller_page_uploading_file_info": "Informații încărcare fișier", "backup_err_only_album": "Nu poți șterge singurul album", "backup_info_card_assets": "resurse", "backup_manual_cancelled": "Anulat", "backup_manual_in_progress": "Încărcarea este deja în curs. Încearcă din nou mai târziu", "backup_manual_success": "Succes", "backup_manual_title": "Status încărcare", + "backup_options_page_title": "Opțiuni Backup", + "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backward": "În sens invers", + "biometric_auth_enabled": "Autentificare biometrică activată", + "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", + "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", + "biometric_not_available": "Autentificarea biometrică nu este disponibilă pe acest dispozitiv", "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vârsta acestei persoane la momentul realizării fotografiei.", "blurred_background": "Fundal neclar", @@ -541,14 +586,19 @@ "camera_model": "Model cameră", "cancel": "Anulați", "cancel_search": "Anulați căutarea", + "canceled": "Anulat", "cannot_merge_people": "Nu se pot îmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", + "cast": "Partajare", + "cast_description": "Configurați destinațiile de difuzare disponibile", "change_date": "Schimbați data", + "change_description": "Schimbă descrierea", + "change_display_order": "Schimbați ordinea de afișare", "change_expiration_time": "Schimbați data expirare", "change_location": "Schimbați locația", "change_name": "Schimbați nume", - "change_name_successfully": "Schimbare nume cu succes", + "change_name_successfully": "Schimbare a numelui făcută cu succes", "change_password": "Schimbați parolă", "change_password_description": "Aceasta este fie prima dată când te conectezi în sistem, fie s-a făcut o solicitare pentru a schimba parola ta. Te rog să introduci noua parolă mai jos.", "change_password_form_confirm_password": "Confirmă parola", @@ -556,8 +606,12 @@ "change_password_form_new_password": "Parolă nouă", "change_password_form_password_mismatch": "Parolele nu se potrivesc", "change_password_form_reenter_new_password": "Reintrodu noua parolă", + "change_pin_code": "Schimbă codul PIN", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", + "check_corrupt_asset_backup": "Verifică copii de rezervă a resurselor corupte", + "check_corrupt_asset_backup_button": "Efectuează verificarea", + "check_corrupt_asset_backup_description": "Rulează această verificare doar prin Wi-Fi și doar după ce toate resursele au fost salvate în copia de rezerva. Procedura poate dura câteva minute.", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", @@ -566,6 +620,14 @@ "clear_all_recent_searches": "Curățați toate căutările recente", "clear_message": "Ștergeți mesajul", "clear_value": "Ștergeți valoarea", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Introdu Parola", + "client_cert_import": "Importă", + "client_cert_import_success_msg": "Certificatul de client este importat", + "client_cert_invalid_msg": "Fisier cu certificat invalid sau parola este greșită", + "client_cert_remove_msg": "Certificatul de client este șters", + "client_cert_subtitle": "Acceptă doar formatul PKCS12 (.p12, .pfx). Importul/ștergerea certificatului este disponibil(ă) doar înainte de autentificare", + "client_cert_title": "Certificat SSL pentru client", "clockwise": "În sensul acelor de ceas", "close": "Închideți", "collapse": "Restrângeți", @@ -578,19 +640,27 @@ "comments_are_disabled": "Comentariile sunt dezactivate", "common_create_new_album": "Creează album nou", "common_server_error": "Te rugăm să verifici conexiunea la rețea, asigura-te că server-ul este accesibil și că versiunile aplicației/server-ului sunt compatibile.", + "completed": "Finalizat", "confirm": "Confirmați", "confirm_admin_password": "Confirmați Parola de Administrator", "confirm_delete_face": "Ești sigur ca vrei sa ștergi {name} din activ?", "confirm_delete_shared_link": "Sunteți sigur că doriți să ștergeți acest link partajat?", "confirm_keep_this_delete_others": "Toate celelalte active din stivă vor fi șterse, cu excepția acestui material. Sunteți sigur că doriți să continuați?", + "confirm_new_pin_code": "Confirmă noul cod PIN", "confirm_password": "Confirmați parola", + "confirm_tag_face": "Vrei să etichetezi această față ca {name}?", + "confirm_tag_face_unnamed": "Vrei să etichetezi această față?", + "connected_device": "Dispozitiv conectat", + "connected_to": "Conectat la", "contain": "Încadrează", + "context": "Context", "continue": "Continuați", "control_bottom_app_bar_create_new_album": "Creează album nou", "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", "control_bottom_app_bar_edit_time": "Editează Data și Ora", + "control_bottom_app_bar_share_link": "Partajează linkul", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută în coș", "copied_image_to_clipboard": "Imagine copiată în clipboard.", @@ -612,6 +682,7 @@ "create_link": "Creează link", "create_link_to_share": "Creează link pentru a distribui", "create_link_to_share_description": "Permiteți oricui are link-ul să vadă fotografia (fotografiile) selectată(e)", + "create_new": "CREARE NOUĂ", "create_new_person": "Creați o persoană nouă", "create_new_person_hint": "Atribuiți resursele selectate unei persoane noi", "create_new_user": "Creează utilizator nou", @@ -621,11 +692,16 @@ "create_tag_description": "Creează o etichetă nouă. Pentru etichete imbricate, te rog să introduci calea completă a etichetei, inclusiv bare oblice (/).", "create_user": "Creează utilizator", "created": "Creat", + "created_at": "Creat", + "crop": "Decupează", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", + "current_pin_code": "Codul PIN actual", + "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele în funcție de limbă și regiune", "dark": "Întunecat", + "dark_theme": "Comută tema întunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", @@ -640,6 +716,7 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele în funcție de regiunea browserului dvs", "delete": "Ștergere", + "delete_action_prompt": "{count} șterse permanent", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -672,6 +749,7 @@ "disallow_edits": "Interzice modificările", "discord": "Server Discord", "discover": "Descoperiți", + "discovered_devices": "Dispozititve descoperite", "dismiss_all_errors": "Ignorați toate erorile", "dismiss_error": "Ignorați eroarea", "display_options": "Opțiuni de afișare", @@ -682,6 +760,12 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_canceled": "Descărcare anulată", + "download_complete": "Descărcare completă", + "download_enqueue": "Descărcare în coadă", + "download_error": "Eroare de descărcare", + "download_failed": "Descărcare eșuată", + "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri încorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile încorporate în fotografiile în mișcare ca fișier separat", "download_settings": "Descărcați", @@ -1204,13 +1288,16 @@ "previous": "Anterior", "previous_memory": "Memoria anterioară", "previous_or_next_photo": "Fotografie înainte/înapoi", + "previous_or_next_year": "An înainte/înapoi", "primary": "Primar", "privacy": "Confidențialitate", + "profile": "Profil", "profile_drawer_app_logs": "Log-uri", - "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", - "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", + "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", + "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", - "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", "profile_picture_set": "Poză de profil setată.", @@ -1230,22 +1317,25 @@ "purchase_failed_activation": "Activare eșuată! Vă rugăm să vă verificați e-mailul pentru cheia de produs corectă!", "purchase_individual_description_1": "Pentru un individ", "purchase_individual_description_2": "Statutul de suporter", + "purchase_individual_title": "Individual", "purchase_input_suggestion": "Aveți o cheie de produs? Introduceți cheia mai jos", "purchase_license_subtitle": "Cumpărați Immich pentru a sprijini dezvoltarea continuă a serviciului", "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", - "purchase_panel_info_1": "Dezvoltarea Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la ea pentru a o face cât se poate de bună. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea, cu alternative reale la serviciile cloud care exploatează.", + "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la el pentru a-l face cât se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", "purchase_remove_server_product_key": "Eliminați cheia de produs a Serverului", "purchase_remove_server_product_key_prompt": "Sigur doriți să eliminați cheia de produs a Serverului?", "purchase_server_description_1": "Pentru tot serverul", "purchase_server_description_2": "Statutul de suporter", + "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", "rating": "Evaluare cu stele", - "rating_clear": "Anulați evaluare", + "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", "rating_description": "Afișați evaluarea EXIF în panoul de informații", "reaction_options": "Opțiuni de reacție", @@ -1256,15 +1346,16 @@ "reassing_hint": "Atribuiți resursele selectate unei persoane existente", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", + "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", "refresh": "Reîmprospătare", - "refresh_encoded_videos": "Actualizează videoclipurile codificate", + "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "Reîmprospătați fețele", "refresh_metadata": "Actualizați metadatele", "refresh_thumbnails": "Reîmprospătați miniaturile", "refreshed": "Reîmprospătat", "refreshes_every_file": "Recitește toate fișierele existente și noi", - "refreshing_encoded_video": "Se reîmprospătează videoclipul codificat", + "refreshing_encoded_video": "Se reîmprospătează videoclipul encodat", "refreshing_faces": "Se reîmprospătează fețele", "refreshing_metadata": "Se reîmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", @@ -1276,9 +1367,12 @@ "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_locked_folder": "Eliminați din folderul securizat", + "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile în biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", "remove_memory": "Șterge amintirea", "remove_photo_from_memory": "Șterge fotografia din această amintire", + "remove_tag": "Eliminați ticheta", "remove_url": "Eliminați adresa URL", "remove_user": "Eliminați utilizatorul", "removed_api_key": "Cheie API eliminată: {name}", @@ -1647,6 +1741,7 @@ "view_previous_asset": "Vizualizați resursa anterioară", "view_qr_code": "Vezi cod QR", "view_stack": "Vizualizați Stiva", + "view_user": "Vizualizare utilizator", "viewer_remove_from_stack": "Șterge din grup", "viewer_stack_use_as_main_asset": "Folosește ca resursă principală", "viewer_unstack": "Anulează grup", @@ -1656,11 +1751,12 @@ "week": "Sǎptǎmânǎ", "welcome": "Bun venit", "welcome_to_immich": "Bun venit la Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nume Wi-Fi", + "wrong_pin_code": "Cod PIN greșit", "year": "An", "years_ago": "acum {years, plural, one {# an} other {# ani}} în urmă", "yes": "Da", "you_dont_have_any_shared_links": "Nu aveți linkuri partajate", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Numele rețelei tale WiFi", "zoom_image": "Măriți Imaginea" } diff --git a/i18n/ru.json b/i18n/ru.json index 5810a31053..84db991027 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Управление настройками метаданных", "migration_job": "Миграция", "migration_job_description": "Перенос миниатюр объектов и лиц в последнюю структуру папок", + "nightly_tasks_cluster_faces_setting_description": "Запустить распознавание людей по новым обнаруженным лицам", + "nightly_tasks_cluster_new_faces_setting": "Распознавание новых лиц", + "nightly_tasks_database_cleanup_setting": "Задачи очистки базы данных", + "nightly_tasks_database_cleanup_setting_description": "Удаление старых и более ненужных записей из базы данных", + "nightly_tasks_generate_memories_setting": "Создание воспоминаний", + "nightly_tasks_generate_memories_setting_description": "Создание новых воспоминаний из существующих объектов", + "nightly_tasks_missing_thumbnails_setting": "Создание отсутствующих миниатюр", + "nightly_tasks_missing_thumbnails_setting_description": "Добавление объектов без миниатюр в очередь для их создания", + "nightly_tasks_settings": "Настройки ночных задач", + "nightly_tasks_settings_description": "Управление ночными регламентными задачами", + "nightly_tasks_start_time_setting": "Время начала", + "nightly_tasks_start_time_setting_description": "Время, когда сервер начинает выполнять ночные задачи", + "nightly_tasks_sync_quota_usage_setting": "Синхронизация квот хранилища", + "nightly_tasks_sync_quota_usage_setting_description": "Обновление квоты хранилища пользователя на основе актуальных данных", "no_paths_added": "Пути не добавлены", "no_pattern_added": "Шаблон не добавлен", "note_apply_storage_label_previous_assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным объектам, запустите", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI редиректа для мобильных", "oauth_mobile_redirect_uri_override": "Перенаправление URI для мобильных устройств", "oauth_mobile_redirect_uri_override_description": "Включите, если поставщик OAuth не разрешает использование мобильного URI, например, ''{callback}''", + "oauth_role_claim": "Утверждение роли", + "oauth_role_claim_description": "Автоматическое предоставление доступа администратора на основе наличия этого утверждения. Утверждение может иметь значение 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Настройки входа через OAuth", "oauth_settings_more_details": "Для получения дополнительной информаций об этой функции обратитесь к документации.", @@ -357,10 +373,12 @@ "admin_password": "Пароль администратора", "administration": "Управление сервером", "advanced": "Расширенные", + "advanced_settings_beta_timeline_subtitle": "Попробуйте новый функционал приложения", + "advanced_settings_beta_timeline_title": "Бета-версия временной шкалы", "advanced_settings_enable_alternate_media_filter_subtitle": "Используйте этот параметр для фильтрации медиафайлов во время синхронизации на основе альтернативных критериев. Пробуйте только в том случае, если у вас есть проблемы с обнаружением приложением всех альбомов.", "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНТАЛЬНО] Использование фильтра синхронизации альбомов альтернативных устройств", "advanced_settings_log_level_title": "Уровень логирования: {level}", - "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные изображения. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", + "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные миниатюры. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", "advanced_settings_prefer_remote_title": "Предпочитать фото на сервере", "advanced_settings_proxy_headers_subtitle": "Определите заголовки прокси-сервера, которые Immich должен отправлять с каждым сетевым запросом", "advanced_settings_proxy_headers_title": "Заголовки прокси", @@ -388,6 +406,7 @@ "album_options": "Параметры альбома", "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", + "album_search_not_found": "Не найдено альбомов по вашему запросу", "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Порядок сортировки в альбомах по умолчанию", "albums_default_sort_order_description": "Первоначальный порядок сортировки, устанавливаемый в новых альбомах.", "albums_feature_description": "Коллекции фото и видео, которыми можно делиться с другими пользователями.", + "albums_on_device_count": "Альбомы на устройстве ({count})", "all": "Все", "all_albums": "Все альбомы", "all_people": "Все люди", @@ -427,6 +447,7 @@ "app_settings": "Параметры приложения", "appears_in": "Добавлено в", "archive": "Архив", + "archive_action_prompt": "{count} добавлено в Архив", "archive_or_unarchive_photo": "Архивировать или разархивировать фото", "archive_page_no_archived_assets": "В архиве сейчас пусто", "archive_page_title": "Архив ({count})", @@ -464,7 +485,6 @@ "assets": "Объекты", "assets_added_count": "{count, plural, one {Добавлен # объект} many {Добавлено # объектов} other {Добавлено # объекта}}", "assets_added_to_album_count": "В альбом {count, plural, one {добавлен # объект} many {добавлено # объектов} other {добавлено # объекта}}", - "assets_added_to_name_count": "{count, plural, one {# объект добавлен} many {# объектов добавлено} other {# объекта добавлено}} в {hasName, select, true {альбом {name}} other {новый альбом}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Объект не может быть добавлен} other {Объекты не могут быть добавлены}} в альбом", "assets_count": "{count, plural, one {# объект} many {# объектов} other {# объекта}}", "assets_deleted_permanently": "{count} объект(ов) удалено навсегда", @@ -587,6 +607,7 @@ "cancel": "Отменить", "cancel_search": "Отменить поиск", "canceled": "Отменено", + "canceling": "Отмена", "cannot_merge_people": "Невозможно объединить людей", "cannot_undo_this_action": "Это действие нельзя отменить!", "cannot_update_the_description": "Невозможно обновить описание", @@ -703,7 +724,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", - "darkTheme": "Переключение темной темы", + "dark_theme": "Тёмная тема", "date_after": "Дата после", "date_and_time": "Дата и Время", "date_before": "Дата до", @@ -713,12 +734,13 @@ "day": "День", "deduplicate_all": "Убрать все дубликаты", "deduplication_criteria_1": "Размер изображения в байтах", - "deduplication_criteria_2": "Подсчет данных EXIF", + "deduplication_criteria_2": "Количество EXIF данных", "deduplication_info": "Информация о дедупликации", - "deduplication_info_description": "Для автоматического предварительного выбора объектов и массового удаления дубликатов мы рассмотрим:", + "deduplication_info_description": "Для автоматического выбора лучших объектов среди дубликатов анализируется следующая информация:", "default_locale": "Дата и время по умолчанию", "default_locale_description": "Использовать формат даты и времени в соответствии с языковым стандартом вашего браузера", "delete": "Удалить", + "delete_action_prompt": "{count} удалено навсегда", "delete_album": "Удалить альбом", "delete_api_key_prompt": "Вы действительно хотите удалить этот API ключ?", "delete_dialog_alert": "Эти элементы будут безвозвратно удалены с сервера, а также с вашего устройства", @@ -732,6 +754,7 @@ "delete_key": "Удалить ключ", "delete_library": "Удалить библиотеку", "delete_link": "Удалить ссылку", + "delete_local_action_prompt": "{count} удалено локально", "delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии", "delete_local_dialog_ok_force": "Все равно удалить", "delete_others": "Удалить остальные", @@ -745,6 +768,7 @@ "description": "Описание", "description_input_hint_text": "Добавить описание...", "description_input_submit_error": "Не удалось обновить описание, проверьте логи, чтобы узнать причину", + "deselect_all": "Снять выделение", "details": "Подробности", "direction": "Направление", "disabled": "Отключено", @@ -762,6 +786,7 @@ "documentation": "Документация", "done": "Готово", "download": "Скачать", + "download_action_prompt": "Загружаются {count} объектов", "download_canceled": "Загрузка отменена", "download_complete": "Загрузка окончена", "download_enqueue": "Загрузка в очереди", @@ -783,7 +808,7 @@ "downloading_media": "Загрузка медиа", "drop_files_to_upload": "Перенесите файлы в любое место для загрузки", "duplicates": "Дубликаты", - "duplicates_description": "Разберитесь с каждой группой, указав, какие из них являются дубликатами, если таковые имеются", + "duplicates_description": "Просмотрите найденные дубликаты и в каждой группе укажите, какие объекты оставить, а какие удалить", "duration": "Продолжительность", "edit": "Редактировать", "edit_album": "Редактировать альбом", @@ -799,6 +824,7 @@ "edit_key": "Изменить ключ", "edit_link": "Редактировать ссылку", "edit_location": "Редактировать местоположение", + "edit_location_action_prompt": "{count} мест изменено", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактировать имя", "edit_people": "Редактировать людей", @@ -817,6 +843,7 @@ "empty_trash": "Очистить корзину", "empty_trash_confirmation": "Вы уверены, что хотите очистить корзину? Все объекты в корзине будут навсегда удалены из Immich.\nВы не сможете отменить это действие!", "enable": "Включить", + "enable_backup": "Включить резервное копирование", "enable_biometric_auth_description": "Введите свой PIN-код для включения биометрической аутентификации", "enabled": "Включено", "end_date": "Дата окончания", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Не удалось загрузить объекты", "failed_to_load_folder": "Ошибка при загрузке папки", "favorite": "Избранное", + "favorite_action_prompt": "{count} добавлено в Избранное", "favorite_or_unfavorite_photo": "Добавить или удалить фотографию из избранного", "favorites": "Избранное", "favorites_page_no_favorites": "В избранном сейчас пусто", @@ -1103,7 +1131,7 @@ "items_count": "{count, plural, one {# элемент} many {# элементов} other {# элемента}}", "jobs": "Задачи", "keep": "Оставить", - "keep_all": "Сохранить всё", + "keep_all": "Сохранить все", "keep_this_delete_others": "Оставить этот, удалить остальные", "kept_this_deleted_others": "Сохранён этот объект и {count, plural, one {# объект удалён} many {# объектов удалено} other {# объекта удалено}}", "keyboard_shortcuts": "Сочетания клавиш", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Недавно созданные", "library_page_sort_last_modified": "Последнее изменение", "library_page_sort_title": "Название альбома", + "licenses": "Лицензии", "light": "Светлая", "like_deleted": "Лайк удален", "link_motion_video": "Ссылка на движущееся видео", @@ -1246,6 +1275,7 @@ "more": "Больше", "move": "Переместить", "move_off_locked_folder": "Переместить из личной папки", + "move_to_lock_folder_action_prompt": "{count} добавлено в Личную папку", "move_to_locked_folder": "Переместить в личную папку", "move_to_locked_folder_confirmation": "Эти фото и видео будут удалены из всех альбомов и будут доступны только в личной папке", "moved_to_archive": "{count, plural, one {# объект перемещён} many {# объектов перемещено} other {# объекта перемещено}} в архив", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Состояние поддержки", "purchase_server_title": "Сервер", "purchase_settings_server_activated": "Ключом продукта управляет администратор сервера", + "queue_status": "В очереди {count}/{total}", "rating": "Рейтинг звёзд", "rating_clear": "Очистить рейтинг", "rating_count": "{count, plural, one {# звезда} many {# звезд} other {# звезды}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Удалить пользовательский диапазон дат", "remove_deleted_assets": "Удаление автономных файлов", "remove_from_album": "Удалить из альбома", + "remove_from_album_action_prompt": "{count} удалено из альбома", "remove_from_favorites": "Удалить из избранного", + "remove_from_lock_folder_action_prompt": "{count} удалено из Личной папки", "remove_from_locked_folder": "Удалить из личной папки", "remove_from_locked_folder_confirmation": "Вы действительно хотите переместить эти фото и видео из личной папки? Они станут доступны в вашей библиотеке.", "remove_from_shared_link": "Удалить из публичной ссылки", @@ -1532,7 +1565,7 @@ "restored_asset": "Восстановленный объект", "resume": "Продолжить", "retry_upload": "Повторить загрузку", - "review_duplicates": "Посмотреть дубликаты", + "review_duplicates": "Разобрать дубликаты", "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", @@ -1607,18 +1640,18 @@ "select": "Выбрать", "select_album_cover": "Выбрать обложку альбома", "select_all": "Выбрать все", - "select_all_duplicates": "Выбрать все дубликаты", + "select_all_duplicates": "Выбрать все для сохранения", "select_all_in": "Выбрать все в {group}", "select_avatar_color": "Выберите цвет аватара", "select_face": "Выбрать лицо", "select_featured_photo": "Выбрать избранное фото", "select_from_computer": "Выберите с компьютера", - "select_keep_all": "Оставить всё выбранное", + "select_keep_all": "Выбрать все для сохранения", "select_library_owner": "Выберите владельца библиотеки", "select_new_face": "Выбрать другое лицо", "select_person_to_tag": "Выделите лицо человека, которого хотите отметить", "select_photos": "Выберите фотографии", - "select_trash_all": "Удалить всё выбранное", + "select_trash_all": "Выбрать все для удаления", "select_user_for_sharing_page_err_album": "Не удалось создать альбом", "selected": "Выбрано", "selected_count": "{count, plural, one {Выбран # объект} many {Выбрано # объектов} other {Выбрано # объекта}}", @@ -1667,6 +1700,7 @@ "settings_saved": "Настройки сохранены", "setup_pin_code": "Создание PIN-кода", "share": "Поделиться", + "share_action_prompt": "Расшарено {count} объектов", "share_add_photos": "Добавить фото", "share_assets_selected": "{count} выбрано", "share_dialog_preparing": "Подготовка...", @@ -1767,10 +1801,11 @@ "sort_recent": "Недавние фото", "sort_title": "Заголовок", "source": "Исходный код", - "stack": "Группировать", - "stack_duplicates": "Группировать дубликаты", + "stack": "Сгруппировать", + "stack_action_prompt": "{count} сгруппировано", + "stack_duplicates": "Сгруппировать дубликаты", "stack_select_one_photo": "Выберите главную фотографию для группы", - "stack_selected_photos": "Группировать выбранные объекты", + "stack_selected_photos": "Сгруппировать выбранные объекты", "stacked_assets_count": "{count, plural, one {# объект объединен} many {# объектов объединено} other {# объекта объединено}} в группу", "stacktrace": "Трассировка стека", "start": "Старт", @@ -1838,7 +1873,8 @@ "total": "Всего", "total_usage": "Общая статистика", "trash": "Корзина", - "trash_all": "Удалить всё", + "trash_action_prompt": "{count} перемещено в корзину", + "trash_all": "Удалить все", "trash_count": "Удалить {count, number}", "trash_delete_asset": "Переместить в корзину", "trash_emptied": "Корзина очищена", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "Ошибка при изменении PIN-кода", "unable_to_setup_pin_code": "Ошибка при создании PIN-кода", "unarchive": "Восстановить", + "unarchive_action_prompt": "{count} удалено из архива", "unarchived_count": "{count, plural, one {# объект возвращён} many {# объектов возвращено} other {# объекта возвращено}} из архива", "undo": "Отменить", "unfavorite": "Удалить из избранного", + "unfavorite_action_prompt": "{count} удалено из избранного", "unhide_person": "Показать человека", "unknown": "Неизвестно", "unknown_country": "Неизвестная страна", @@ -1872,15 +1910,18 @@ "unnamed_share": "Общий доступ без названия", "unsaved_change": "Несохранённое изменение", "unselect_all": "Отменить выделение", - "unselect_all_duplicates": "Отменить выбор всех дубликатов", + "unselect_all_duplicates": "Выбрать все для удаления", "unselect_all_in": "Отменить выделение в {group}", "unstack": "Разгруппировать", + "unstack_action_prompt": "{count} разгруппировано", "unstacked_assets_count": "{count, plural, one {Разгруппирован # объект} many {Разгруппировано # объектов} other {Разгруппировано # объекта}}", + "untagged": "Без тегов", "up_next": "Следующее", "updated_at": "Обновлён", "updated_password": "Пароль изменён", "upload": "Загрузить", "upload_concurrency": "Параллельность загрузки", + "upload_details": "Подробности загрузки", "upload_dialog_info": "Хотите загрузить выбранные объекты на сервер?", "upload_dialog_title": "Загрузить объект", "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные объекты.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Посмотреть статистику использования аккаунта", "username": "Имя пользователя", "users": "Пользователи", + "users_added_to_album_count": "{count, plural, one {# пользователь добавлен} many {# пользователей добавлено} other {# пользователя добавлено}} к альбому", "utilities": "Утилиты", "validate": "Проверить", "validate_endpoint_error": "Введите корректный URL", @@ -1930,6 +1972,7 @@ "view_album": "Просмотреть альбом", "view_all": "Посмотреть всё", "view_all_users": "Показать всех пользователей", + "view_details": "Посмотреть подробности", "view_in_timeline": "Показать на временной шкале", "view_link": "Показать ссылку", "view_links": "Показать ссылки", @@ -1937,7 +1980,7 @@ "view_next_asset": "Показать следующий объект", "view_previous_asset": "Показать предыдущий объект", "view_qr_code": "Посмотреть QR код", - "view_stack": "Показать стек", + "view_stack": "Показать группу", "view_user": "Просмотреть пользователя", "viewer_remove_from_stack": "Убрать из группы", "viewer_stack_use_as_main_asset": "Использовать в качестве основного объекта", diff --git a/i18n/sk.json b/i18n/sk.json index cd64c52532..c8c1cb5303 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,7 @@ "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", + "add_endpoint": "Pridať koncový bod", "add_exclusion_pattern": "Pridať vzor vylúčenia", "add_import_path": "Pridať cestu pre import", "add_location": "Pridať polohu", @@ -33,6 +34,7 @@ "added_to_favorites_count": "Pridané {count, number} do obľúbených", "admin": { "add_exclusion_pattern_description": "Pridávanie vzorov na vylúčenie. Globovanie pomocou *, ** a ? je podporované. Ak chcete ignorovať všetky súbory v akomkoľvek adresári s názvom \"Raw\", použite \"**/Raw/**\". Ak chcete ignorovať všetky súbory končiace na \".tif\", použite \"**/*.tif\". Ak chcete ignorovať absolútnu cestu, použite príkaz \"/cesta/k/ignorovanym/**\".", + "admin_user": "Správca", "asset_offline_description": "Táto položka externej knižnice sa už na disku nenachádza a bola presunutá do koša. Pokiaľ bol súbor presunutý v rámci knižnice, skontrolujte časovú os a vyhľadajte nové odpovedajúce položky. Ak chcete túto položku obnoviť, uistite sa, že je cesta k nižšie uvedenému súboru prístupná pre aplikáciu Immich a prehľadajte knižnicu.", "authentication_settings": "Overovanie a prihlásenie", "authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia", @@ -47,7 +49,7 @@ "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", - "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# contained asset} other {all # contained assets}} súborov z Immich. Súbory budú ponechané na disku.", + "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", @@ -55,14 +57,14 @@ "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", "cron_expression_description": "Nastavte interval skenovania pomocou formátu cron. Pre viac informácií navštívte Crontab Guru", - "cron_expression_presets": "Presety cron výrazov", + "cron_expression_presets": "Predvoľby výrazov Cron", "disable_login": "Zakázať prihlásenie", - "duplicate_detection_job_description": "Spustiť strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", + "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", "external_library_management": "Správa Externej Knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia položky aktív, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupovať rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Resetovať“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -84,7 +86,7 @@ "image_resolution_description": "Vyššie rozlíšenie môže zachovať viac detailov, ale kódovanie trvá dlhšie, súbory sú väčšie a môže to znížiť rýchlosť odozvy aplikácie.", "image_settings": "Obrázky", "image_settings_description": "Spravovať kvalitu a rozlíšenie generovaných obrázkov", - "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, používané pri zobrazovaní skupín fotiek ako na hlavnej časovej osi", + "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, ktorá sa používa pri prezeraní skupín fotografií ako na hlavnej časovej osi", "image_thumbnail_quality_description": "Kvalita miniatúry v stupnici od 1 do 100. Vyššia hodnota znamená lepšiu kvalitu, ale produkuje väčšie súbory a môže znížiť odozvu aplikácie.", "image_thumbnail_title": "Miniatúry", "job_concurrency": "Súbežnosť úlohy - {job}", @@ -103,18 +105,18 @@ "library_scanning_enable_description": "Zapnúť pravidelné skenovanie knižnice", "library_settings": "Externá knižnica", "library_settings_description": "Spravovať nastavenia externej knižnice", - "library_tasks_description": "Vyhľadávanie nových alebo zmenených položiek v externých knižniciach", + "library_tasks_description": "Vyhľadajte nové alebo zmenené médiá v externých knižniciach", "library_watching_enable_description": "Sledovať externé knižnice pre zmeny v súboroch", "library_watching_settings": "Sledovanie knižnice (EXPERIMENTÁLNE)", "library_watching_settings_description": "Automaticky sledovať zmenené súbory", - "logging_enable_description": "Povoliť logovanie", - "logging_level_description": "Ak je povolené, akú úroveň logovania použiť.", - "logging_settings": "Logovanie", + "logging_enable_description": "Povoliť ukladanie záznamov", + "logging_level_description": "Ak je povolené, akú úroveň záznamov použiť.", + "logging_settings": "Ukladanie záznamov", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Názov modelu CLIP je uvedený tu. Pamätajte, že pri zmene modelu je nutné znovu spustiť úlohu 'Inteligentné vyhľadávanie' pre všetky obrázky.", "machine_learning_duplicate_detection": "Detekcia duplikátov", "machine_learning_duplicate_detection_enabled": "Povoliť detekciu duplikátov", - "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, presne identické položky budú stále deduplikované.", + "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, úplne identické položky budú stále deduplikované.", "machine_learning_duplicate_detection_setting_description": "Použiť CLIP embeddings na identifikáciu pravdepodobných duplikátov", "machine_learning_enabled": "Povoliť strojové učenie", "machine_learning_enabled_description": "Ak je vypnuté, všetky funkcie strojového učenia (ML) budú vypnuté, bez ohľadu na nastavenia nižšie.", @@ -124,10 +126,10 @@ "machine_learning_facial_recognition_model_description": "Modely sú zoradené od najväčšieho po najmenší. Väčšie modely sú pomalšie a vyžadujú viac pamäte, ale poskytujú lepšie výsledky. Pamätajte, že po zmene modelu je potrebné znovu spustiť úlohu detekcie tvárí pre všetky obrázky.", "machine_learning_facial_recognition_setting": "Povoliť rozpoznávanie tvárí", "machine_learning_facial_recognition_setting_description": "Ak je vypnuté, obrázky nebudú spracované pre rozpoznávanie tvárí a nebudú sa zobrazovať v sekcii Ľudia na stránke Preskúmať.", - "machine_learning_max_detection_distance": "Maximálna detekčná odchylka", - "machine_learning_max_detection_distance_description": "Maximálna odchylka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", - "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchylka", - "machine_learning_max_recognition_distance_description": "Maximálna odchylka medzi dvoma tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", + "machine_learning_max_detection_distance": "Maximálna detekčná odchýlka", + "machine_learning_max_detection_distance_description": "Maximálna odchýlka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", + "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchýlka", + "machine_learning_max_recognition_distance_description": "Maximálna odchýlka medzi dvomi tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", "machine_learning_min_detection_score": "Minimálne detekčné skóre", "machine_learning_min_detection_score_description": "Minimálne skóre dôveryhodnosti pre detekciu tváre v rozsahu od 0 do 1. Nižšie hodnoty odhalia viac tvárí, ale môžu viesť k falošným pozitivním výsledkom.", "machine_learning_min_recognized_faces": "Minimum rozpoznaných tvárí", @@ -140,11 +142,11 @@ "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", "manage_concurrency": "Správa súbežnosti", - "manage_log_settings": "Spravovať nastavenia logovania", + "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", - "map_gps_settings": "Mapa & GPS", - "map_gps_settings_description": "Správa nastavení máp a GPS reverzného geokódovania", + "map_gps_settings": "Mapa a nastavenia GPS", + "map_gps_settings_description": "Spravujte nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", @@ -157,16 +159,30 @@ "memory_cleanup_job": "Vymazávanie spomienok", "memory_generate_job": "Vytváranie spomienok", "metadata_extraction_job": "Extrahovať metadáta", - "metadata_extraction_job_description": "Vytiahne metadáta z každej položky, ako napríklad GPS, tváre a rozlíšenie", + "metadata_extraction_job_description": "Získajte informácie o metadátach z každého média, ako sú GPS, tváre a rozlíšenie", "metadata_faces_import_setting": "Povoliť import tváre", - "metadata_faces_import_setting_description": "Importuj tváre z EXIF dát obrázkov a sidecar súborov", + "metadata_faces_import_setting_description": "Importovať tváre z EXIF údajov obrázka a pridružených súborov", "metadata_settings": "Metadáta", "metadata_settings_description": "Spravovať nastavenia metadát", "migration_job": "Migrácia", - "migration_job_description": "Migrácia miniatúr položiek a tvárí na najnovšiu štruktúru priečinkov", + "migration_job_description": "Presunúť miniatúry pre médiá a tváre do najnovšej štruktúry priečinkov", + "nightly_tasks_cluster_faces_setting_description": "Spustiť rozpoznávanie tváre na novo-zistených tvárach", + "nightly_tasks_cluster_new_faces_setting": "Zoskupiť nové tváre", + "nightly_tasks_database_cleanup_setting": "Úlohy čistenia databázy", + "nightly_tasks_database_cleanup_setting_description": "Vyčistiť databázu od starých, neplatných údajov", + "nightly_tasks_generate_memories_setting": "Vytvoriť spomienky", + "nightly_tasks_generate_memories_setting_description": "Vytvoriť nové spomienky z položiek", + "nightly_tasks_missing_thumbnails_setting": "Vytvoriť chýbajúce náhľady", + "nightly_tasks_missing_thumbnails_setting_description": "Zaradiť položky bez náhľadov do poradia na vytvorenie náhľadov", + "nightly_tasks_settings": "Nastavenia nočných úloh", + "nightly_tasks_settings_description": "Spravovať nočné úlohy", + "nightly_tasks_start_time_setting": "Čas spustenia", + "nightly_tasks_start_time_setting_description": "Čas, kedy server začne vykonávať nočné úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizovať využitie kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovať kvótu úložiska používateľa na základe aktuálneho využitia", "no_paths_added": "Neboli pridané žiadne cesty", "no_pattern_added": "Nebol pridaný žiadny vzor", - "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť Štítkovanie úložiska na predtým nahrané aktíva, spustite príkaz", + "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť štítok úložiska na predtým nahrané položky, spustite príkaz", "note_cannot_be_changed_later": "POZNÁMKA: Toto nie je možné neskôr zmeniť!", "notification_email_from_address": "Z adresy", "notification_email_from_address_description": "E-mailová adresa odosielateľa, napríklad: \"Immich Foto Server \". Uistite sa, že používate adresu, z ktorej máte povolené odosielať e-maily.", @@ -189,19 +205,24 @@ "oauth_auto_register": "Automatická regristrácia", "oauth_auto_register_description": "Automatické zaregistrovanie nového požívateľa pri prihlásení pomocou OAuth", "oauth_button_text": "Text tlačítka", + "oauth_client_secret_description": "Vyžaduje sa, ak poskytovateľ OAuth nepodporuje PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Prihlásiť sa pomocou OAuth", "oauth_mobile_redirect_uri": "URI mobilného presmerovania", "oauth_mobile_redirect_uri_override": "Prepísanie URI mobilného presmerovania", "oauth_mobile_redirect_uri_override_description": "Povoľte, keď poskytovateľ protokolu OAuth nepovoľuje identifikátor URI pre mobilné zariadenia, napríklad ''{callback}''", + "oauth_role_claim": "Požiadavka na rolu", + "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", - "oauth_storage_label_claim_description": "Automaticky nastaviť Štítok úložiska používateľa na hodnotu tohto nároku.", + "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", "oauth_storage_quota_claim_description": "Automaticky nastaviť kvótu úložiska používateľa na hodnotu tejto deklarácie.", "oauth_storage_quota_default": "Predvolený limit úložiska (GiB)", "oauth_storage_quota_default_description": "Kvóta v GiB, ktorá sa použije, ak nie je poskytnutá žiadna požiadavka.", + "oauth_timeout": "Časový limit požiadavky", + "oauth_timeout_description": "Časový limit pre požiadavky v milisekundách", "password_enable_description": "Prihlásiť sa pomocou emailu a hesla", "password_settings": "Prihlásenie cez heslo", "password_settings_description": "Spravovať nastavenia prihlásenia cez heslo", @@ -225,8 +246,8 @@ "server_settings_description": "Spravovať nastavenia servera", "server_welcome_message": "Uvítacia správa", "server_welcome_message_description": "Správa, ktorá sa zobrazí na prihlasovacej stránke.", - "sidecar_job": "Sidecar metadáta", - "sidecar_job_description": "Objavte alebo synchronizujte metadáta Sidecar zo súborového systému", + "sidecar_job": "Pridružené metadáta", + "sidecar_job_description": "Objavte alebo synchronizujte pridružené metadáta zo súborového systému", "slideshow_duration_description": "Čas zobrazenia obrázku v sekundách", "smart_search_job_description": "Spustite strojové učenie na médiách na podporu inteligentného vyhľadávania", "storage_template_date_time_description": "Časová pečiatka vytvorenia položky sa používa pre informácie o dátume a čase", @@ -239,13 +260,14 @@ "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", "storage_template_settings_description": "Spravujte štruktúru priečinkov a názov súboru odovzdaného média", "storage_template_user_label": "{label} je Štítok úložiska používateľa", "system_settings": "Nastavenia systému", - "tag_cleanup_job": "Premazanie značiek", - "template_email_available_tags": "V šablóne môžeš použiť nasledujúce stítky: {tags}", + "tag_cleanup_job": "Prečistenie štítkov", + "template_email_available_tags": "V šablóne môžete použiť nasledujúce premenné: {tags}", "template_email_if_empty": "Ak nie je zadaná žiadna šablóna, bude použitá predvolená šablóna.", "template_email_invite_album": "Šablóna Pozvánky do albumu", "template_email_preview": "Ukážka", @@ -259,19 +281,19 @@ "theme_settings": "Motívy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", "thumbnail_generation_job": "Generovať Miniatúry", - "thumbnail_generation_job_description": "Generuje veľké, malé a rozostrení miniatúry pre každú položku, ako aj miniatúry pre každú osobu", + "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", - "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude interagovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", + "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", "transcoding_acceleration_nvenc": "NVENC (vyžaduje NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (vyžaduje 7. generáciu Intel CPU alebo novšiu)", "transcoding_acceleration_rkmpp": "RKMPP (iba na Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Akceptované zvukové kodeky", - "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_containers": "Akceptované kontajnery", - "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_video_codecs": "Akceptované video kodeky", - "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_advanced_options_description": "Možnosti, ktoré by väčšina používateľov nemala meniť", "transcoding_audio_codec": "Zvukový kodek", "transcoding_audio_codec_description": "Opus je najkvalitnejšia možnosť, ale má nižšiu kompatibilitu so starými zariadeniami alebo softvérom.", @@ -281,7 +303,7 @@ "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", - "transcoding_disabled_description": "Neprekódujte žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", + "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", "transcoding_encoding_options": "Možnosti kódovania", "transcoding_encoding_options_description": "Nastavte kodeky, rozlíšenie, kvalitu a ďalšie možnosti pre kódované videá", "transcoding_hardware_acceleration": "Hardvérová akcelerácia", @@ -295,7 +317,7 @@ "transcoding_max_keyframe_interval": "Maximálny interval medzi kľúčovými snímkami", "transcoding_max_keyframe_interval_description": "Nastavuje maximálnu vzdialenosť medzi kľúčovými snímkami. Nižšie hodnoty zhoršujú účinnosť kompresie, ale zlepšujú časy vyhľadávania a môžu zlepšiť kvalitu v scénach s rýchlym pohybom. Hodnota 0 nastavuje túto hodnotu automaticky.", "transcoding_optimal_description": "Videá s vyšším ako cieľovým rozlíšením alebo videá, ktoré nie sú v prijateľnom formáte", - "transcoding_policy": "Politika prekódovania", + "transcoding_policy": "Pravidlá prekódovania", "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", @@ -304,7 +326,7 @@ "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", - "transcoding_settings": "Transkódovania videa", + "transcoding_settings": "Nastavenia prekódovania videa", "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", @@ -314,22 +336,22 @@ "transcoding_threads_description": "Vyššie hodnoty vedú k rýchlejšiemu kódovaniu, ale ponechávajú serveru menej priestoru na spracovanie iných úloh počas aktivity. Táto hodnota by nemala byť väčšia ako počet jadier CPU. Maximalizuje využitie, ak je nastavená na hodnotu 0.", "transcoding_tone_mapping": "Tónové mapovanie", "transcoding_tone_mapping_description": "Snaží sa zachovať vzhľad videí HDR pri konverzii na SDR. Každý algoritmus robí rôzne kompromisy v oblasti farieb, detailov a jasu. Hable zachováva detaily, Mobius zachováva farby a Reinhard zachováva jas.", - "transcoding_transcode_policy": "Politika prekódovania", - "transcoding_transcode_policy_description": "Zásady, kedy sa má video prekódovať. Videá HDR sa vždy prekódujú (okrem prípadov, keď je prekódovanie vypnuté).", + "transcoding_transcode_policy": "Pravidlá prekódovania", + "transcoding_transcode_policy_description": "Pravidlá, kedy sa má video prekódovať. HDR videá sa prekódujú vždy (okrem prípadov, keď je prekódovanie vypnuté).", "transcoding_two_pass_encoding": "Dvojpriechodové kódovanie", - "transcoding_two_pass_encoding_setting_description": "Prekladajte v dvoch priechodoch, aby ste vytvorili lepšie zakódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je max bitrate vypnutý.", + "transcoding_two_pass_encoding_setting_description": "Prekódovať v dvoch fázach, aby sa vytvorili lepšie kódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je maximálny bitrate vypnutý.", "transcoding_video_codec": "Video kodek", - "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekódováva, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", + "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekóduje, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", "trash_enabled_description": "Povoliť funkcie koša", "trash_number_of_days": "Počet dní", - "trash_number_of_days_description": "Počet dní, počas ktorých sa má majetok ponechať v koši pred jeho trvalým odstránením", + "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", "trash_settings": "Kôš", "trash_settings_description": "Spravovať nastavenia koša", "user_cleanup_job": "Premazanie používateľov", - "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# day} other {# days}}.", + "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a aktív používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", - "user_delete_immediately": "Konto a médiá {user} budú zaradené do frontu na trvalé vymazanie okamžite.", + "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a médií používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", "user_management": "Správa používateľov", @@ -351,11 +373,19 @@ "admin_password": "Administrátorské heslo", "administration": "Administrácia", "advanced": "Pokročilé", - "advanced_settings_log_level_title": "Úroveň logovania: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektoré zariadenia sú extrémne pomalé pre načítavanie miniatúr z fotiek na zariadení. Povoľte toto nastavenie aby sa namiesto toho načítavali obrázky zo servera.", - "advanced_settings_prefer_remote_title": "Preferovať vzdialené obrázky", + "advanced_settings_beta_timeline_subtitle": "Vyskúšajte prostredie novej aplikácie", + "advanced_settings_beta_timeline_title": "Beta verzia časovej osi", + "advanced_settings_enable_alternate_media_filter_subtitle": "Túto možnosť použite na filtrovanie médií počas synchronizácie na základe alternatívnych kritérií. Túto možnosť vyskúšajte len vtedy, ak máte problémy s detekciou všetkých albumov v aplikácii.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNE] Použiť alternatívny filter synchronizácie albumu zariadenia", + "advanced_settings_log_level_title": "Úroveň ukladania záznamov: {level}", + "advanced_settings_prefer_remote_subtitle": "V niektorých zariadeniach sa miniatúry z miestnych položiek načítavajú veľmi pomaly. Aktivovaním tohto nastavenia sa namiesto toho načítajú vzdialené obrázky.", + "advanced_settings_prefer_remote_title": "Uprednostniť vzdialené obrázky", + "advanced_settings_proxy_headers_subtitle": "Určite hlavičky proxy servera, ktoré by mal Immich posielať s každou požiadavkou na sieť", + "advanced_settings_proxy_headers_title": "Proxy hlavičky", "advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikátom zo strany servera. Vyžaduje sa pre samo-podpísané certifikáty.", "advanced_settings_self_signed_ssl_title": "Povoliť samo-podpísané SSL certifikáty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky vymazať alebo obnoviť položku na tomto zariadení, keď sa táto akcia vykoná na webe", + "advanced_settings_sync_remote_deletions_title": "Synchronizovať vzdialené vymazania [EXPERIMENTÁLNE]", "advanced_settings_tile_subtitle": "Pokročilé nastavenia používateľa", "advanced_settings_troubleshooting_subtitle": "Povoliť ďalšie funkcie pre opravu chýb", "advanced_settings_troubleshooting_title": "Oprava chýb", @@ -376,6 +406,7 @@ "album_options": "Nastavenia albumu", "album_remove_user": "Odstrániť používateľa?", "album_remove_user_confirmation": "Ste si istý, že chcete odstrániť používateľa {user}?", + "album_search_not_found": "Neboli nájdené žiadne albumy zodpovedajúce vášmu hľadaniu", "album_share_no_users": "Vyzerá to, že ste tento album zdieľali so všetkými používateľmi alebo nemáte žiadneho používateľa, s ktorým by ste ho mohli zdieľať.", "album_updated": "Album bol aktualizovaný", "album_updated_setting_description": "Obdržať e-mailové upozornenie, keď v zdieľanom albume pribudnú nové položky", @@ -391,15 +422,19 @@ "album_viewer_page_share_add_users": "Pridať používateľov", "album_with_link_access": "Umožnite komukoľvek s odkazom pozrieť si fotky a ľudí v tomto albume.", "albums": "Albumy", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumov}}", + "albums_count": "{count, plural, one {{count, number} album} few {{count, number} albumy} other {{count, number} albumov}}", + "albums_default_sort_order": "Predvolené poradie albumov", + "albums_default_sort_order_description": "Počiatočné poradie triedenia položiek pri vytváraní nových albumov.", + "albums_feature_description": "Zbierky médií, ktoré možno zdieľať s ostatnými používateľmi.", + "albums_on_device_count": "Albumy v zariadení ({count})", "all": "Všetko", "all_albums": "Všetky albumy", "all_people": "Všetci ľudia", "all_videos": "Všetky videa", "allow_dark_mode": "Povoliť tmavý režim", "allow_edits": "Povoliť úpravy", - "allow_public_user_to_download": "Povoľte verejnému používateľovi sťahovať", - "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrávať", + "allow_public_user_to_download": "Povoliť verejnému používateľovi stiahnutie", + "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrať", "alt_text_qr_code": "Obrázok QR kódu", "anti_clockwise": "Proti smeru hodinových ručičiek", "api_key": "API Klúč", @@ -409,9 +444,10 @@ "app_bar_signout_dialog_content": "Skutočne sa chcete odhlásiť?", "app_bar_signout_dialog_ok": "Áno", "app_bar_signout_dialog_title": "Odhlásiť sa", - "app_settings": "Nastavenia Aplikácie", + "app_settings": "Nastavenia aplikácie", "appears_in": "Vyskytuje sa v", - "archive": "Archivovať", + "archive": "Archív", + "archive_action_prompt": "{count} pridaných do archívu", "archive_or_unarchive_photo": "Archivácia alebo odarchivovanie fotografie", "archive_page_no_archived_assets": "Žiadne archivované médiá", "archive_page_title": "Archív ({count})", @@ -439,31 +475,45 @@ "asset_list_settings_title": "Fotografická mriežka", "asset_offline": "Médium je offline", "asset_offline_description": "Toto externý obsah sa už nenachádza na disku. Požiadajte o pomoc svojho správcu Immich.", + "asset_restored_successfully": "Položky boli úspešne obnovené", "asset_skipped": "Preskočené", "asset_skipped_in_trash": "V koši", "asset_uploaded": "Nahrané", "asset_uploading": "Nahráva sa…", - "asset_viewer_settings_title": "Zobrazovač položiek", + "asset_viewer_settings_subtitle": "Spravujte nastavenia prehliadača galérie", + "asset_viewer_settings_title": "Prehliadač médií", "assets": "Položky", "assets_added_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položek}}", "assets_added_to_album_count": "Do albumu {count, plural, one {bola pridaná # položka} few {boli pridané # položky} other {bolo pridaných # položiek}}", - "assets_added_to_name_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položiek}} do {hasName, select, true {alba {name}} other {nového albumu}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {položku} other {položiek}} nie je možné pridať do albumu", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", + "assets_deleted_permanently": "{count} položka(iek) natrvalo vymazaná(ých)", + "assets_deleted_permanently_from_server": "{count} položka(iek) natrvalo vymazaná(ých) zo servera Immich", + "assets_downloaded_failed": "{count, plural, one {Stiahnutý # súbor - {error} súbor zlyhal} few {Stiahnuté # súbory - {error} súbory zlyhali} other {Stiahnutých # súborov - {error} súborov zlyhalo}}", + "assets_downloaded_successfully": "{count, plural, one {# súbor bol úspešne stiahnutý} few {# súbory boli úspešne stiahnuté} other {# súborov bolo úspešne stiahnutých}}", "assets_moved_to_trash_count": "Do koša {count, plural, one {bola presunutá # položka} few {boli presunuté # položky} other {bolo presunutých # položiek}}", "assets_permanently_deleted_count": "Trvalo {count, plural, one {vymazaná # položka} few {vymazané # položky} other {vymazaných # položiek}}", "assets_removed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", + "assets_removed_permanently_from_device": "{count} položka(iek) natrvalo vymazaná(ých) z vášho zariadenia", "assets_restore_confirmation": "Naozaj chcete obnoviť všetky vyhodené položky? Túto akciu nie je možné vrátiť späť! Upozorňujeme, že týmto spôsobom nie je možné obnoviť žiadne offline položky.", "assets_restored_count": "{count, plural, one {Obnovená # položka} few {Obnovené # položky} other {Obnovených # položiek}}", - "assets_restored_successfully": "{count} medií úspešne obnovených", + "assets_restored_successfully": "{count} médií úspešne obnovených", + "assets_trashed": "{count} položka(iek) vyhodená(ých) do koša", "assets_trashed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", - "assets_were_part_of_album_count": "{count, plural, one {Položka bola} other {Položky boli}} súčasťou albumu", + "assets_trashed_from_server": "{count} položka(iek) vyhodená(ých) do koša zo servera Immich", + "assets_were_part_of_album_count": "{count, plural, one {Položka už bola} other {Položky už boli}} súčasťou albumu", "authorized_devices": "Autorizované zariadenia", + "automatic_endpoint_switching_subtitle": "Pripojiť sa lokálne prostredníctvom určeného pripojenia Wi-Fi, ak je k dispozícii, a používať alternatívne pripojenia inde", + "automatic_endpoint_switching_title": "Automatické prepínanie URL adresy", + "autoplay_slideshow": "Automatické prehrávanie prezentácie", "back": "Späť", "back_close_deselect": "Späť, zavrieť alebo zrušiť výber", + "background_location_permission": "Povolenie na určenie polohy na pozadí", + "background_location_permission_content": "Aby bolo možné prepínať siete pri spustení na pozadí, musí mať aplikácia Immich *vždy* presný prístup k polohe, aby mohla prečítať názov siete Wi-Fi", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na položku ju zahrniete, dvojitým ťuknutím ju vylúčite", "backup_album_selection_page_assets_scatter": "Súbory môžu byť roztrúsené vo viacerých albumoch. To umožňuje zahrnúť alebo vylúčiť albumy počas procesu zálohovania.", - "backup_album_selection_page_select_albums": "Vybrané albumy", + "backup_album_selection_page_select_albums": "Vybrať albumy", "backup_album_selection_page_selection_info": "Informácie o výbere", "backup_album_selection_page_total_assets": "Celkový počet jedinečných súborov", "backup_all": "Všetko", @@ -485,7 +535,7 @@ "backup_controller_page_background_charging": "Len počas nabíjania", "backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovať službu na pozadí", "backup_controller_page_background_delay": "Oneskorenie zálohovania nových médií: {duration}", - "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových aktív bez nutnosti otvorenia aplikácie", + "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových položiek bez nutnosti otvorenia aplikácie", "backup_controller_page_background_is_off": "Automatické zálohovanie na pozadí je vypnuté", "backup_controller_page_background_is_on": "Automatické zálohovanie na pozadí je zapnuté", "backup_controller_page_background_turn_off": "Vypnúť zálohovanie na pozadí", @@ -503,7 +553,7 @@ "backup_controller_page_info": "Informácie o zálohovaní", "backup_controller_page_none_selected": "Žiadne vybrané", "backup_controller_page_remainder": "Zostáva", - "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výbraných albumov", + "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výberu", "backup_controller_page_server_storage": "Serverové úložisko", "backup_controller_page_start_backup": "Spustiť zálohovanie", "backup_controller_page_status_off": "Automatické zálohovanie na popredí je vypnuté", @@ -521,22 +571,27 @@ "backup_manual_success": "Úspech", "backup_manual_title": "Stav nahrávania", "backup_options_page_title": "Možnosti zálohovania", - "backward": "Spätne", + "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", + "backward": "Dozadu", + "biometric_auth_enabled": "Biometrické overovanie je povolené", + "biometric_locked_out": "Ste vymknutí z biometrického overovania", + "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", + "biometric_not_available": "Biometrické overenie nie je v tomto zariadení k dispozícii", "birthdate_saved": "Dátum narodenia bol úspešne uložený", "birthdate_set_description": "Dátum narodenia sa používa na výpočet veku tejto osoby v čase fotografie.", "blurred_background": "Rozmazané pozadie", "bugs_and_feature_requests": "Chyby a požiadavky na funkcie", "build": "Zostava", "build_image": "Obraz zostavy", - "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikáte položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", - "bulk_keep_duplicates_confirmation": "Naozaj chceš ponechať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto sa vysporiadaš so všetkými duplicitnými skupinami bez mazania súborov.", - "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto si ponecháš z každej skupiny najväčší súbor a vymažeš všetky ostatné duplicitné súbory v skupine.", + "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikátne položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", + "bulk_keep_duplicates_confirmation": "Naozaj chcete ponechať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa vyriešia všetky duplicitné skupiny bez toho, aby sa čokoľvek odstránilo.", + "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplicitné položky sa vyhodia.", "buy": "Kúpiť Immich", "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", - "cache_settings_duplicated_assets_title": "Duplikáty ({count})", + "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", "cache_settings_statistics_shared": "Zdieľané náhľady albumov", @@ -552,9 +607,12 @@ "cancel": "Zrušiť", "cancel_search": "Zrušiť vyhľadávanie", "canceled": "Zrušené", + "canceling": "Ruší sa", "cannot_merge_people": "Nie je možné zlúčiť ľudí", "cannot_undo_this_action": "Túto akciu nemôžete vrátiť späť!", "cannot_update_the_description": "Popis nie je možné aktualizovať", + "cast": "Prenos (cast)", + "cast_description": "Nastavte dostupné ciele prenosu", "change_date": "Upraviť dátum", "change_description": "Zmeniť popis", "change_display_order": "Zmeniť poradie zobrazenia", @@ -572,7 +630,9 @@ "change_pin_code": "Zmeniť PIN kód", "change_your_password": "Zmeňte si heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", + "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", + "check_corrupt_asset_backup_description": "Spustiť túto kontrolu len cez Wi-Fi a po zálohovaní všetkých položiek. Tento postup môže trvať niekoľko minút.", "check_logs": "Skontrolovať logy", "choose_matching_people_to_merge": "Vyberte rovnakých ľudí na zlúčenie", "city": "Mesto", @@ -584,6 +644,11 @@ "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Zadať heslo", "client_cert_import": "Importovať", + "client_cert_import_success_msg": "Certifikát klienta je naimportovaný", + "client_cert_invalid_msg": "Neplatný súbor certifikátu alebo nesprávne heslo", + "client_cert_remove_msg": "Certifikát klienta je odstránený", + "client_cert_subtitle": "Podporuje iba formát PKCS12 (.p12, .pfx). Importovanie/odstránenie certifikátu je k dispozícii len pred prihlásením", + "client_cert_title": "SSL certifikát klienta", "clockwise": "V smere hodinových ručičiek", "close": "Zatvoriť", "collapse": "Zbaliť", @@ -607,7 +672,8 @@ "confirm_tag_face": "Chcete označiť túto tvár ako {name}?", "confirm_tag_face_unnamed": "Chcete označiť túto tvár?", "connected_device": "Pripojené zariadenie", - "contain": "Obsiahnúť", + "connected_to": "Pripojené k", + "contain": "Prispôsobiť", "context": "Kontext", "continue": "Pokračovať", "control_bottom_app_bar_create_new_album": "Vytvoriť nový album", @@ -616,6 +682,7 @@ "control_bottom_app_bar_edit_location": "Upraviť polohu", "control_bottom_app_bar_edit_time": "Upraviť dátum a čas", "control_bottom_app_bar_share_link": "Zdieľať odkaz", + "control_bottom_app_bar_share_to": "Zdieľať cez", "control_bottom_app_bar_trash_from_immich": "Presunúť do koša", "copied_image_to_clipboard": "Obrázok skopírovaný do schránky.", "copied_to_clipboard": "Skopírované do schránky!", @@ -627,7 +694,7 @@ "copy_password": "Skopírovať heslo", "copy_to_clipboard": "Skopírovať do schránky", "country": "Krajina", - "cover": "Titulka", + "cover": "Vyplniť", "covers": "Dlaždice", "create": "Vytvoriť", "create_album": "Vytvoriť album", @@ -635,15 +702,15 @@ "create_library": "Vytvoriť knižnicu", "create_link": "Vytvoriť odkaz", "create_link_to_share": "Vytvoriť odkaz na zdieľanie", - "create_link_to_share_description": "Umožniť každému kto má odkaz zobraziť vybrané fotografie", + "create_link_to_share_description": "Umožniť každému, kto má odkaz, zobraziť vybrané fotografie", "create_new": "VYTVORIŤ NOVÉ", "create_new_person": "Vytvoriť novú osobu", "create_new_person_hint": "Priradiť vybrané položky novej osobe", "create_new_user": "Vytvorenie nového používateľa", "create_shared_album_page_share_add_assets": "Pridať položky", "create_shared_album_page_share_select_photos": "Vybrať fotografie", - "create_tag": "Vytvoriť značku", - "create_tag_description": "Vytvorenie nového štítku. Pre Vnorené štítky, prosím, zadaj celú cestu štítku, vrátane lomítok vpred.", + "create_tag": "Vytvoriť štítok", + "create_tag_description": "Vytvorte nový štítok. V prípade vnorených štítkov zadajte celú cestu k štítku vrátane lomiek.", "create_user": "Vytvoriť používateľa", "created": "Vytvorené", "created_at": "Vytvorené", @@ -657,6 +724,7 @@ "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", + "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", "date_before": "Dátum pred", @@ -669,14 +737,15 @@ "deduplication_criteria_2": "Počet EXIF údajov", "deduplication_info": "Info o deduplikácii", "deduplication_info_description": "Na automatický predvýber položiek a hromadné odstránenie duplicít, sa pozeráme do:", - "default_locale": "Predvolená Lokalizácia", - "default_locale_description": "Formátovanie dátumu a čísel podľa lokalizácie vášho prehliadača", + "default_locale": "Predvolené miestne nastavenie", + "default_locale_description": "Formátovanie dátumov a čísel na základe miestneho nastavenia prehliadača", "delete": "Vymazať", + "delete_action_prompt": "{count} natrvalo vymazaných", "delete_album": "Odstrániť album", "delete_api_key_prompt": "Naozaj chcete odstrániť tento API kľúč?", "delete_dialog_alert": "Tieto položky budú natrvalo odstránené z aplikácie Immich a z vášho zariadenia", "delete_dialog_alert_local": "Tieto položky budú permanentne vymazané z vašeho zariadenia, ale budú stále k dispozícií na serveri Immich", - "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immichi a budú permanentne vymazané z vášho zariadenia", + "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immich a budú permanentne odstránené z vášho zariadenia", "delete_dialog_alert_remote": "Tieto položky budú permanentne vymazané zo serveru Immich", "delete_dialog_ok_force": "Napriek tomu vymazať", "delete_dialog_title": "Vymazať natrvalo", @@ -685,12 +754,13 @@ "delete_key": "Odstrániť kľúč", "delete_library": "Vymazať knižnicu", "delete_link": "Odstrániť odkaz", + "delete_local_action_prompt": "{count} vymazané lokálne", "delete_local_dialog_ok_backed_up_only": "Vymazať len zálohované", "delete_local_dialog_ok_force": "Napriek tomu vymazať", "delete_others": "Vymazať ostatné", "delete_shared_link": "Odstrániť zdieľaný odkaz", "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", - "delete_tag": "Odstrániť označenie", + "delete_tag": "Odstrániť štítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrániť štítok menom {tagName}?", "delete_user": "Vymazať používateľa", "deleted_shared_link": "Vymazaný zdieľaný odkaz", @@ -698,6 +768,7 @@ "description": "Popis", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", + "deselect_all": "Zrušiť výber všetkých", "details": "Podrobnosti", "direction": "Smer", "disabled": "Vypnuté", @@ -715,22 +786,27 @@ "documentation": "Dokumentácia", "done": "Hotovo", "download": "Stiahnuť", + "download_action_prompt": "Sťahuje sa {count} položiek", "download_canceled": "Stiahnutie zrušené", "download_complete": "Stiahnutie dokončené", + "download_enqueue": "Stiahnutie v poradí", "download_error": "Chyba sťahovania", "download_failed": "Stiahnutie sa nepodarilo", "download_finished": "Stiahnutie dokončené", "download_include_embedded_motion_videos": "Vložené videá", "download_include_embedded_motion_videos_description": "Zahrnúť videá vložené do pohyblivých fotiek ako samostatné súbory", + "download_notfound": "Stiahnutie nebolo nájdené", "download_paused": "Stiahnutie pozastavené", "download_settings": "Stiahnuť", "download_settings_description": "Spravovať nastavenia súvisiace so sťahovaním položiek", "download_started": "Sťahovanie spustené", + "download_sucess": "Stiahnutie úspešné", "download_sucess_android": "Médiá boli stiahnuté do DCIM/Immich", + "download_waiting_to_retry": "Čaká sa na opakovanie pokusu", "downloading": "Sťahuje sa", "downloading_asset_filename": "Sťahuje sa položka {filename}", "downloading_media": "Sťahovanie médií", - "drop_files_to_upload": "Hoď súbory kdekoľvek, nahrajú sa", + "drop_files_to_upload": "Umiestnite súbory kamkoľvek na nahratie", "duplicates": "Duplikáty", "duplicates_description": "Vysporiadať sa s každou skupinou tak, že sa duplicitné označia ako duplicitné", "duration": "Trvanie", @@ -748,10 +824,11 @@ "edit_key": "Upraviť kľúč", "edit_link": "Upraviť odkaz", "edit_location": "Upraviť polohu", + "edit_location_action_prompt": "{count} poloha upravená", "edit_location_dialog_title": "Poloha", "edit_name": "Upraviť meno", "edit_people": "Upraviť osoby", - "edit_tag": "Upraiť značku", + "edit_tag": "Upraviť štítok", "edit_title": "Upraviť názov", "edit_user": "Upraviť používateľa", "edited": "Upravené", @@ -766,23 +843,28 @@ "empty_trash": "Vyprázdniť kôš", "empty_trash_confirmation": "Naozaj chcete vyprázdniť kôš? Nenávratne sa vymažú všetky položky z Immich.\nTáto akcia sa nedá vrátiť!", "enable": "Aktivovať", + "enable_backup": "Povoliť zálohovanie", + "enable_biometric_auth_description": "Zadajte svoj PIN kód, aby ste povolili biometrické overenie", "enabled": "Aktivovaný", "end_date": "Koncový dátum", + "enqueued": "V poradí", "enter_wifi_name": "Zadajte názov Wi-Fi", "enter_your_pin_code": "Zadajte svoj PIN kód", "enter_your_pin_code_subtitle": "Zadaním kódu PIN získate prístup k zamknutému priečinku", "error": "Chyba", + "error_change_sort_album": "Nepodarilo sa zmeniť poradie albumu", "error_delete_face": "Chyba pri odstraňovaní tváre z položky", "error_loading_image": "Nepodarilo sa načítať obrázok", "error_saving_image": "Chyba: {error}", + "error_tag_face_bounding_box": "Chyba pri označovaní tváre - nemožno získať súradnice ohraničujúceho poľa", "error_title": "Chyba - niečo sa pokazilo", "errors": { "cannot_navigate_next_asset": "Nedokážem prejsť na ďaľšiu položku", "cannot_navigate_previous_asset": "Nedokážem prejsť na predošlú položku", "cant_apply_changes": "Nedokážem aplikovať zmeny", - "cant_change_activity": "Nodokážem {enabled, select, true {zakázať} other {povoliť}} aktivitu", + "cant_change_activity": "Nie je možné {enabled, select, true {zakázať} other {povoliť}} aktivitu", "cant_change_asset_favorite": "Nedokážem zmeniť obľúbenosť pre položku", - "cant_change_metadata_assets_count": "Nedokážem zmeniť metadáta pre {count, plural, one {# túto položku} other {# tieto položky}}", + "cant_change_metadata_assets_count": "Nie je možné zmeniť metadáta pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "cant_get_faces": "Nedokážem získať tváre", "cant_get_number_of_comments": "Nedokážem získať počet komentárov", "cant_search_people": "Nedokážem hľadať osoby", @@ -802,10 +884,12 @@ "failed_to_keep_this_delete_others": "Nepodarilo sa ponechať túto položku a vymazať tie ostatné položky", "failed_to_load_asset": "Nepodarilo sa načítať položku", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_notifications": "Nepodarilo sa načítať oznámenia", "failed_to_load_people": "Nepodarilo sa načítať ľudí", "failed_to_remove_product_key": "Nepodarilo sa odstrániť produktový kľúč", "failed_to_stack_assets": "Nepodarilo sa zoskupiť položky", "failed_to_unstack_assets": "Nepodarilo sa rozdeliť položky", + "failed_to_update_notification_status": "Nepodarilo sa aktualizovať stav oznámenia", "import_path_already_exists": "Táto cesta importu už existuje.", "incorrect_email_or_password": "Nesprávny e-mail alebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta zlyhala} few {# cesty zlyhali} other {# ciest zlyhalo}} pri validácii", @@ -826,14 +910,14 @@ "unable_to_change_favorite": "Nie je možné zmeniť obľúbené pre položku", "unable_to_change_location": "Nie je možné zmeniť polohu", "unable_to_change_password": "Nie je možné zmeniť heslo", - "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} other {# ľudí}}", + "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "unable_to_complete_oauth_login": "Nemožno dokončiť prihlásenie cez OAuth", "unable_to_connect": "Nie je možné sa pripojiť", "unable_to_copy_to_clipboard": "Nie je možné kopírovať do schránky, overte si, že stránku navštevujete cez https", - "unable_to_create_admin_account": "Nie je možné vytvoriť admin účet", + "unable_to_create_admin_account": "Nie je možné vytvoriť účet správcu", "unable_to_create_api_key": "Nie je možné vytvoriť nový API Klúč", "unable_to_create_library": "Nie je možné vytvoriť knihovňu", - "unable_to_create_user": "Nie je možné vytvoriť uživateľa", + "unable_to_create_user": "Nie je možné vytvoriť používateľa", "unable_to_delete_album": "Nie je možné vymazať album", "unable_to_delete_asset": "Nie je možné vymazať položku", "unable_to_delete_assets": "Chyba pri odstraňovaní položiek", @@ -842,7 +926,7 @@ "unable_to_delete_shared_link": "Nie je možné vymazať zdieľaný odkaz", "unable_to_delete_user": "Nie je možné vymazať používateľa", "unable_to_download_files": "Nie je možné stiahnuť súbory", - "unable_to_edit_exclusion_pattern": "Nie je možné upravit vzorec vylúčenia", + "unable_to_edit_exclusion_pattern": "Nie je možné upraviť vzorec vylúčenia", "unable_to_edit_import_path": "Nie je možné upraviť cestu importu", "unable_to_empty_trash": "Nie je možné vyprázdniť kôš", "unable_to_enter_fullscreen": "Nie je možné prejsť do režimu celej obrazovky", @@ -900,6 +984,7 @@ "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ĽUDIA", "exif_bottom_sheet_person_add_person": "Pridať meno", + "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", "exit_slideshow": "Opustiť Slideshow", @@ -908,7 +993,7 @@ "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", "experimental_settings_subtitle": "Používajte na vlastné riziko!", "experimental_settings_title": "Experimentálne", - "expire_after": "Expiruje po", + "expire_after": "Platnosť vyprší", "expired": "Vypršalo", "expires_date": "Expiruje {date}", "explore": "Preskúmať", @@ -922,17 +1007,20 @@ "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikácia sa pripojí k serveru prostredníctvom prvej z nižšie uvedených adries URL, na ktorú sa dostane, počnúc zhora nadol", "face_unassigned": "Nepriradená", "failed": "Neúspešné", + "failed_to_authenticate": "Nepodarilo sa overiť", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_folder": "Nepodarilo sa načítať priečinok", "favorite": "Obľúbené", + "favorite_action_prompt": "{count} pridané do obľúbených", "favorite_or_unfavorite_photo": "Označiť fotku ako obľúbenú alebo neobľúbenú", "favorites": "Obľúbené", "favorites_page_no_favorites": "Žiadne obľúbené médiá", "feature_photo_updated": "Hlavný obrázok bol aktualizovaný", "features": "Funkcie", "features_setting_description": "Spravovať funkcie aplikácie", - "file_name": "Meno súboru", + "file_name": "Názov súboru", "file_name_or_extension": "Názov alebo prípona súboru", - "filename": "Meno súboru", + "filename": "Názov súboru", "filetype": "Typ súboru", "filter": "Filter", "filter_people": "Filtrovať ľudí", @@ -940,11 +1028,15 @@ "find_them_fast": "Nájdite ich rýchlejšie podľa mena", "fix_incorrect_match": "Opraviť nesprávnu zhodu", "folder": "Priečinok", + "folder_not_found": "Priečinok nebol nájdený", "folders": "Priečinky", - "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na súborovom systéme", + "folders_feature_description": "Prezeranie zobrazenia priečinkov fotografií a videí v systéme súborov", "forward": "Dopredu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Táto funkcia načítava externé zdroje zo spoločnosti Google, aby mohla fungovať.", "general": "Všeobecné", "get_help": "Získať pomoc", + "get_wifiname_error": "Nepodarilo sa získať názov Wi-Fi siete. Uistite sa, že ste udelili potrebné oprávnenia a ste pripojení k sieti Wi-Fi", "getting_started": "Začíname", "go_back": "Vrátiť sa späť", "go_to_folder": "Prejsť do priečinka", @@ -959,6 +1051,12 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "header_settings_add_header_tip": "Pridať hlavičku", + "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", + "header_settings_header_name_input": "Názov hlavičky", + "header_settings_header_value_input": "Hodnota hlavičky", + "headers_settings_tile_subtitle": "Určite hlavičky proxy servera, ktoré má aplikácia posielať s každou požiadavkou na sieť", + "headers_settings_tile_title": "Vlastné hlavičky proxy servera", "hi_user": "Ahoj {name} ({email})", "hide_all_people": "Skryť všetky osoby", "hide_gallery": "Skryť galériu", @@ -974,25 +1072,31 @@ "home_page_archive_err_partner": "Nemožno archivovať partnerské položky, preskakuje sa", "home_page_building_timeline": "Vytváranie časovej osi", "home_page_delete_err_partner": "Nie je možné vymazať položky partnera, preskakuje sa", + "home_page_delete_remote_err_local": "Miestne položky vo výbere vzdialeného odstránenia, preskakuje sa", "home_page_favorite_err_local": "Zatiaľ nie je možné zaradiť lokálne média medzi obľúbené, preskakuje sa", "home_page_favorite_err_partner": "Na teraz nemôžete pridať partnerove médiá medzi obľúbené", "home_page_first_time_notice": "Ak aplikáciu používate prvýkrát, uistite sa, že ste si vybrali záložný album, aby sa na časovej osi mohli zobrazovať fotografie a videá", + "home_page_locked_error_local": "Nie je možné presunúť miestne položky do zamknutého priečinka, preskakuje sa", + "home_page_locked_error_partner": "Nie je možné presunúť partnerské položky do zamknutého priečinka, preskakuje sa", "home_page_share_err_local": "Nemožno zdieľať lokálne médiá pomocou odkazu", "home_page_upload_err_limit": "Naraz môžete nahrať len 30 médií, preskakuje sa", "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", + "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} nasnímané {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} dňa {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} a {person2} dňa {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {person3} dňa {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {additionalCount, number} inými dňa {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Obrázok}} nasnímané v {city}, {country} dňa {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Obrázok}} v {city}, {country} s {person1} a {person2} zo dňa {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímaný v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} s osobami {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} dňa {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} s {person1} a {person2} dňa {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video nasnímamé} other {Obrázok odfotený}} v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_saved_successfully": "Obrázok bol uložený", "image_viewer_page_state_provider_download_started": "Sťahovanie sa začalo", "image_viewer_page_state_provider_download_success": "Sťahovanie bolo úspešné", "image_viewer_page_state_provider_share_error": "Chyba zdieľania", @@ -1015,16 +1119,26 @@ "night_at_twoam": "Každú noc o 2:00" }, "invalid_date": "Neplatný dátum", + "invalid_date_format": "Neplatný formát dátumu", "invite_people": "Pozvať ľudí", "invite_to_album": "Pozvať do albumu", + "ios_debug_info_fetch_ran_at": "Načítanie prebehlo {dateTime}", + "ios_debug_info_last_sync_at": "Posledná synchronizácia {dateTime}", + "ios_debug_info_no_processes_queued": "Žiadne procesy nie sú v poradí na pozadí", + "ios_debug_info_no_sync_yet": "Zatiaľ nebola spustená žiadna úloha synchronizácie na pozadí", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí v poradí} few {{count} procesy na pozadí v poradí} other {{count} procesov na pozadí v poradí}}", + "ios_debug_info_processing_ran_at": "Spracovanie prebehlo {dateTime}", "items_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", "jobs": "Úlohy", "keep": "Ponechať", "keep_all": "Ponechať všetko", "keep_this_delete_others": "Ponechať toto, odstrániť ostatné", - "kept_this_deleted_others": "Ponechá túto položku a odstráni {count, plural, one {# položku} other {# položiek}}", + "kept_this_deleted_others": "Ponechal túto položku a odstránil {count, plural, one {# položku} few {# položky} other {# položiek}}", "keyboard_shortcuts": "Klávesové skratky", "language": "Jazyk", + "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", + "language_no_results_title": "Neboli nájdené žiadne jazyky", + "language_search_hint": "Vyhľadať jazyky...", "language_setting_description": "Vyberte preferovaný jazyk", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", @@ -1041,6 +1155,7 @@ "library_page_sort_created": "Najnovšie vytvorené", "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", + "licenses": "Licencie", "light": "Svetlý", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", @@ -1050,7 +1165,10 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", "local_network": "Miestna sieť", + "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", + "location_permission": "Povolenie na určenie polohy", "location_permission_content": "Na používanie funkcie automatického prepínania potrebuje aplikácia Immich presné povolenie na určenie polohy, aby mohla prečítať názov aktuálnej Wi-Fi siete", "location_picker_choose_on_map": "Zvoľte na mape", "location_picker_latitude_error": "Zadajte platnú zemepisnú šírku", @@ -1061,6 +1179,7 @@ "locked_folder": "Zamknutý priečinok", "log_out": "Odhlásiť sa", "log_out_all_devices": "Odhlásiť všetky zariadenia", + "logged_in_as": "Prihlásený ako {user}", "logged_out_all_devices": "Všetky zariadenia odhlásené", "logged_out_device": "Zariadenie odhlásené", "login": "Prihlásenie", @@ -1069,7 +1188,7 @@ "login_form_back_button_text": "Späť", "login_form_email_hint": "tvojmail@email.com", "login_form_endpoint_hint": "http://ip-tvojho-servera:port", - "login_form_endpoint_url": "URL adresa servera", + "login_form_endpoint_url": "URL adresa koncového bodu servera", "login_form_err_http": "Prosím, uveďte http:// alebo https://", "login_form_err_invalid_email": "Neplatný e-mail", "login_form_err_invalid_url": "Neplatná URL adresa", @@ -1089,7 +1208,7 @@ "logout_all_device_confirmation": "Ste si istý, že sa chcete odhlásiť zo všetkých zariadení?", "logout_this_device_confirmation": "Ste si istý, že sa chcete odhlásiť z tohoto zariadenia?", "longitude": "Zemepisná dĺžka", - "look": "Zobrazenie", + "look": "Vzhľad", "loop_videos": "Opakovať videá", "loop_videos_description": "Povolí prehrávanie videí v slučke v detailnom zobrazení.", "main_branch_warning": "Používate vývojársku verziu; dôrazne odporúčame používať vydané verzie!", @@ -1127,6 +1246,9 @@ "map_settings_only_show_favorites": "Zobraziť iba obľúbené", "map_settings_theme_settings": "Téma mapy", "map_zoom_to_see_photos": "Oddiaľte priblíženie aby ste videli fotky", + "mark_all_as_read": "Označiť všetko ako prečítané", + "mark_as_read": "Označiť ako prečítané", + "marked_all_as_read": "Označené všetko ako prečítané", "matches": "Zhody", "media_type": "Typ média", "memories": "Spomienky", @@ -1143,7 +1265,7 @@ "merge_people_limit": "Zlúčiť môžete naraz najviac 5 tvárí", "merge_people_prompt": "Chcete zlúčiť týchto ľudí? Táto akcia sa nedá vrátiť.", "merge_people_successfully": "Zlúčenie ľudí sa podarilo", - "merged_people_count": "Zlúčení {count, plural, one {# človek} other {# ľudia}}", + "merged_people_count": "{count, plural, one {Zlúčená # osoba} few {Zlúčené # osoby} other {Zlúčených # osôb}}", "minimize": "Minimalizovať", "minute": "Minúta", "missing": "Chýbajúce", @@ -1153,15 +1275,20 @@ "more": "Viac", "move": "Presunúť", "move_off_locked_folder": "Presunúť zo zamknutého priečinka", + "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", + "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", "multiselect_grid_edit_date_time_err_read_only": "Nemožno upraviť dátum položky len na čítanie, preskakujem", - "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek) len na čítanie, preskakuje sa", + "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek), ktorá je len na čítanie, preskakuje sa", "mute_memories": "Vyblednutie spomienok", "my_albums": "Moje albumy", "name": "Meno", "name_or_nickname": "Meno alebo prezývka", + "networking_settings": "Sieť", + "networking_subtitle": "Spravovať nastavenia koncového bodu servera", "never": "nikdy", "new_album": "Nový album", "new_api_key": "Nový API kľúč", @@ -1178,9 +1305,10 @@ "no_albums_message": "Vytvorí album na organizovanie fotiek a videí", "no_albums_with_name_yet": "Vyzerá, že zatiaľ nemáte album s týmto názvom.", "no_albums_yet": "Vyzerá, že zatiaľ nemáte žiadne albumy.", - "no_archived_assets_message": "Archivovať fotografie a videá, aby sa skryli zo zobrazenia Fotografie", + "no_archived_assets_message": "Archivujte fotografie a videá a skryte ich z vášho zobrazenia fotografií", "no_assets_message": "KLIKNITE A NAHRAJTE SVOJU PRVÚ FOTKU", "no_assets_to_show": "Žiadne položky", + "no_cast_devices_found": "Nenašli sa žiadne zariadenia na prenos", "no_duplicates_found": "Nenašli sa žiadne duplicity.", "no_exif_info_available": "Nie sú dostupné exif údaje", "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vašej zbierky.", @@ -1188,16 +1316,20 @@ "no_libraries_message": "Vytvorí externú knižnicu na prezeranie fotiek a videí", "no_locked_photos_message": "Fotografie a videá v zamknutom priečinku sú skryté a nezobrazujú sa pri prehľadávaní alebo vyhľadávaní v knižnici.", "no_name": "Bez mena", + "no_notifications": "Žiadne oznámenia", + "no_people_found": "Nenašli sa žiadni vyhovujúci ľudia", "no_places": "Bez miesta", "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", "not_in_any_album": "Nie je v žiadnom albume", + "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", "notes": "Poznámky", + "nothing_here_yet": "Zatiaľ tu nič nie je", "notification_permission_dialog_content": "Ak chcete povoliť upozornenia, prejdite do Nastavenia a vyberte možnosť Povoliť.", - "notification_permission_list_tile_content": "Udeľte oprávnenie k aktivácii oznámení.", - "notification_permission_list_tile_enable_button": "Povoliť upozornenia", + "notification_permission_list_tile_content": "Udeľte povolenie na zapnutie oznámení.", + "notification_permission_list_tile_enable_button": "Povoliť oznámenia", "notification_permission_list_tile_title": "Povolenie oznámení", "notification_toggle_setting_description": "Povoliť e-mailové upozornenia", "notifications": "Oznámenia", @@ -1209,7 +1341,9 @@ "oldest_first": "Najstaršie prvé", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", + "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", + "onboarding_server_welcome_description": "Nastavme vašu inštanciu pomocou bežných nastavení.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1247,9 +1381,9 @@ "password_required": "Heslo je povinné", "password_reset_success": "Obnovenie hesla úspešné", "past_durations": { - "days": "{days, plural, one {Posledný deň} other {Posledných # dní }}", - "hours": "{hours, plural, one {Posledná hodina} other {Posledných # hodín}}", - "years": "{years, plural, one {Posledný rok} other {Posledné # roky}}" + "days": "{days, plural, one {Posledný deň} few {Posledné # dni} other {Posledných # dní }}", + "hours": "{hours, plural, one {Posledná hodina} few {Posledné # hodiny} other {Posledných # hodín}}", + "years": "{years, plural, one {Posledný rok} few {Posledné # roky} other {Posledných # rokov}}" }, "path": "Cesta", "pattern": "Vzor", @@ -1258,17 +1392,18 @@ "paused": "Pozastavené", "pending": "Čakajúce", "people": "Ľudia", - "people_edits_count": "{count, plural, one {Upravená # osoba} other {Upravených # ľudí}}", + "people_edits_count": "{count, plural, one {Upravená # osoba} few {Upravené # osoby} other {Upravených # osôb}}", "people_feature_description": "Prehliadanie fotiek a videí zoskupených podľa ľudí", - "people_sidebar_description": "Zobrazí odkaz na Ľudí v bočnom paneli", + "people_sidebar_description": "Zobraziť odkaz na Ľudí v bočnom paneli", "permanent_deletion_warning": "Varovanie o trvalom zmazaní", "permanent_deletion_warning_setting_description": "Zobraziť varovanie pri trvalom zmazaní položky", "permanently_delete": "Trvalo zmazať", - "permanently_delete_assets_count": "Navždy zmazať {count, plural, one {položku} other {položky}}", - "permanently_delete_assets_prompt": "Naozaj si prajete navždy zmazať {count, plural, one {túto položku?} other {týchto # položiek?}} Vymažú sa aj {count, plural, one {zo svojho albumu} other {zo svojich albumov}}.", + "permanently_delete_assets_count": "Natrvalo vymazať {count, plural, one {položku} few {položky} other {položiek}}", + "permanently_delete_assets_prompt": "Ste si istí, že chcete natrvalo vymazať {count, plural, one {túto položku?} few {tieto # položky?} other {týchto # položiek?}} Týmto sa odstráni aj {count, plural, one {z jej albumu} other {zo svojich albumov}}.", "permanently_deleted_asset": "Navždy odstránená položka", - "permanently_deleted_assets_count": "Navždy {count, plural, one {odstránená # položka} other {odstránené # položky}}", + "permanently_deleted_assets_count": "Natrvalo {count, plural, one {odstránená # položka} few {odstránené # položky} other {odstránených # položiek}}", "permission": "Povolenie", + "permission_empty": "Vaše povolenie by nemalo byť prázdne", "permission_onboarding_back": "Späť", "permission_onboarding_continue_anyway": "Pokračovať aj tak", "permission_onboarding_get_started": "Začať", @@ -1283,9 +1418,13 @@ "photo_shared_all_users": "Vyzerá, že zdieľate svoje fotky so všetkými používateľmi alebo nemáte žiadnych používateľov.", "photos": "Fotografie", "photos_and_videos": "Fotografie & Videa", - "photos_count": "{count, plural, one {{count, number} Fotka} other {{count, number} Fotiek}}", + "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotiek}}", "photos_from_previous_years": "Fotky z minulých rokov", "pick_a_location": "Vyberte polohu", + "pin_code_changed_successfully": "Úspešne ste zmenili PIN kód", + "pin_code_reset_successfully": "Úspešne ste obnovili PIN kód", + "pin_code_setup_successfully": "Úspešne ste nastavili PIN kód", + "pin_verification": "Overenie PIN kódom", "place": "Miesto", "places": "Miesta", "places_count": "{count, plural, one {{count, number} miesto} few {{count, number} miesta} other {{count, number} miest}}", @@ -1293,17 +1432,22 @@ "play_memories": "Prehrať spomienky", "play_motion_photo": "Prehrať pohyblivú fotku", "play_or_pause_video": "Pustí alebo pozastaví video", + "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_title": "Preferencie", + "preferences_settings_subtitle": "Spravujte predvoľby aplikácie", + "preferences_settings_title": "Predvoľby", "preset": "Prednastavenie", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", + "previous_or_next_day": "Deň dopredu/dozadu", + "previous_or_next_month": "Mesiac dopredu/dozadu", "previous_or_next_photo": "Fotka ďalšia/predošlá", + "previous_or_next_year": "Rok dopredu/dozadu", "primary": "Primárne", "privacy": "Súkromie", "profile": "Profil", - "profile_drawer_app_logs": "Logy", + "profile_drawer_app_logs": "Záznamy", "profile_drawer_client_out_of_date_major": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_out_of_date_minor": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_server_up_to_date": "Klient a server sú aktuálne", @@ -1327,7 +1471,7 @@ "purchase_button_select": "Vybrať", "purchase_failed_activation": "Aktivácia sa nepodarila! Prosím skontrolujte email či je správny kľúč produktu!", "purchase_individual_description_1": "Pre jednotlivca", - "purchase_individual_description_2": "Stav podporovateľa", + "purchase_individual_description_2": "Štatút podporovateľa", "purchase_individual_title": "Jednotlivec", "purchase_input_suggestion": "Máte produktový kľúč? Zadajte ho nižšie", "purchase_license_subtitle": "Kúpte si Immich a podporte neustály vývoj tejto služby", @@ -1343,22 +1487,24 @@ "purchase_remove_server_product_key": "Odstrániť produktový kľúč servera", "purchase_remove_server_product_key_prompt": "Naozaj chcete odstrániť produktový kľúč servera?", "purchase_server_description_1": "Pre celý server", - "purchase_server_description_2": "Stav podporovateľa", + "purchase_server_description_2": "Štatút podporovateľa", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový kľúč servera spravuje admin", + "queue_status": "V poradí {count}/{total}", "rating": "Hodnotenie hviezdičkami", "rating_clear": "Vyčistiť hodnotenie", - "rating_count": "{count, plural, one {# hviezdička} other {# hviezdičky}}", - "rating_description": "Zobrazí EXIF hodnotenie v info paneli", + "rating_count": "{count, plural, one {# hviezdička} few {# hviezdičky} other {# hviezdičiek}}", + "rating_description": "Zobraziť EXIF hodnotenie v informačnom paneli", "reaction_options": "Možnosti reakcie", "read_changelog": "Prečítať zoznam zmien", "reassign": "Preradiť", - "reassigned_assets_to_existing_person": "Preradené {count, plural, one {# položka} other {# položky}} k {name, select, null {existujúcej osobe} other {{name}}}", - "reassigned_assets_to_new_person": "Preradené {count, plural, one {# položka} other {# položiek}} novej osobe", + "reassigned_assets_to_existing_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} k {name, select, null {existujúcej osobe} other {{name}}}", + "reassigned_assets_to_new_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} novej osobe", "reassing_hint": "Priradí zvolenú položku k existujúcej osobe", "recent": "Nedávne", "recent-albums": "Posledné albumy", "recent_searches": "Posledné vyhľadávania", + "recently_added": "Nedávno pridané", "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", @@ -1367,20 +1513,22 @@ "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", "refresh_thumbnails": "Obnoviť miniatúry", - "refreshed": "Aktualizované", + "refreshed": "Obnovené", "refreshes_every_file": "Znova prečíta všetky existujúce a nové súbory", "refreshing_encoded_video": "Obnovovanie enkódovaných videí", - "refreshing_faces": "Obnovovnie tvárí", + "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", "remove": "Odstrániť", - "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položky} other {# položiek}} z albumu?", - "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} other {# položiek}} z tohoto zdieľaného odkazu?", + "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", + "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", "remove_assets_title": "Odstrániť položky?", "remove_custom_date_range": "Odstrániť vlastný rozsah dátumov", "remove_deleted_assets": "Odstrániť vymazané položky", "remove_from_album": "Odstrániť z albumu", + "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", + "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", @@ -1395,7 +1543,7 @@ "removed_from_favorites_count": "{count, plural, other {Odstránených #}} z obľúbených", "removed_memory": "Odstránená pamäť", "removed_photo_from_memory": "Fotografia odstránená z pamäte", - "removed_tagged_assets": "Odstránená značka z {count, plural, one {# položky} other {# položiek}}", + "removed_tagged_assets": "Odstránený štítok z {count, plural, one {# položky} other {# položiek}}", "rename": "Premenovať", "repair": "Opraviť", "repair_no_results_message": "Nesledované a chýbajúce súbory sa zobrazia tu", @@ -1422,6 +1570,7 @@ "role_editor": "Editor", "role_viewer": "Divák", "save": "Uložiť", + "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", "saved_settings": "Uložené nastavenia", @@ -1445,9 +1594,11 @@ "search_filter_apply": "Použiť filter", "search_filter_camera_title": "Vyberte typ kamery", "search_filter_date": "Dátum", + "search_filter_date_interval": "{start} do {end}", "search_filter_date_title": "Vyberte rozsah dátumov", "search_filter_display_option_not_in_album": "Mimo albumu", "search_filter_display_options": "Možnosti zobrazenia", + "search_filter_filename": "Hľadať podľa názvu súboru", "search_filter_location": "Poloha", "search_filter_location_title": "Vyberte polohu", "search_filter_media_type": "Typ média", @@ -1490,6 +1641,7 @@ "select_album_cover": "Vyberte obal albumu", "select_all": "Vybrať všetko", "select_all_duplicates": "Vybrať všetky duplikáty", + "select_all_in": "Označiť všetky v {group}", "select_avatar_color": "Vyberte farbu avatara", "select_face": "Vyberte tvár", "select_featured_photo": "Vyberte náhľadovú fotku", @@ -1502,15 +1654,17 @@ "select_trash_all": "Vybrať zahodiť všetky", "select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriť album", "selected": "Vybrané", - "selected_count": "{count, plural, other {# vybrané}}", + "selected_count": "{count, plural, one {# vybraná} few {# vybrané} other {# vybraných}}", "send_message": "Odoslať správu", "send_welcome_email": "Odoslať uvítací e-mail", + "server_endpoint": "Koncový bod servera", "server_info_box_app_version": "Verzia aplikácie", - "server_info_box_server_url": "URL Serveru", + "server_info_box_server_url": "URL adresa servera", "server_offline": "Server je Offline", "server_online": "Server je Online", - "server_stats": "Serverové Štatistiky", - "server_version": "Verzia Servera", + "server_privacy": "Zásady ochrany osobných údajov servera", + "server_stats": "Štatistiky servera", + "server_version": "Verzia servera", "set": "Nastaviť", "set_as_album_cover": "Nastaviť ako obal albumu", "set_as_featured_photo": "Nastaviť ako hlavnú fotku", @@ -1518,14 +1672,16 @@ "set_date_of_birth": "Nastaviť dátum narodenia", "set_profile_picture": "Nastaviť profilový obrázok", "set_slideshow_to_fullscreen": "Nastaviť prezentáciu na celú obrazovku", - "setting_image_viewer_help": "Prehliadač detailov najprv načíta malú miniatúru, potom načíta náhľad strednej veľkosti (ak je povolený) a nakoniec načíta originál (ak je povolený).", + "set_stack_primary_asset": "Nastaviť ako primárnu položku", + "setting_image_viewer_help": "V detailnom prehliadači sa najprv načíta malá miniatúra, potom sa načíta stredne veľký náhľad (ak je povolený) a nakoniec sa načíta originál (ak je povolený).", "setting_image_viewer_original_subtitle": "Povolením umožníte načítať pôvodný obrázok v plnom rozlíšení (veľký!). Zakázaním znížite používania dát (v sieti, aj v dočasnej pamäte zariadenia).", "setting_image_viewer_original_title": "Načítať pôvodný obrázok", "setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.", "setting_image_viewer_preview_title": "Načítať náhľad obrázka", "setting_image_viewer_title": "Obrázky", "setting_languages_apply": "Použiť", - "setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {duration}", + "setting_languages_subtitle": "Zmeniť jazyk aplikácie", + "setting_notifications_notify_failures_grace_period": "Upozorniť na zlyhanie zálohovania na pozadí: {duration}", "setting_notifications_notify_hours": "{count} hodín", "setting_notifications_notify_immediately": "okamžite", "setting_notifications_notify_minutes": "{count} minút", @@ -1533,14 +1689,18 @@ "setting_notifications_notify_seconds": "{count} sekúnd", "setting_notifications_single_progress_subtitle": "Podrobné informácie o priebehu nahrávania pre položku", "setting_notifications_single_progress_title": "Zobraziť priebeh detailov zálohovania na pozadí", - "setting_notifications_subtitle": "Prispôsobenie predvolieb oznámení", + "setting_notifications_subtitle": "Upravte svoje nastavenia oznámení", "setting_notifications_total_progress_subtitle": "Celkový priebeh nahrávania (nahraných/celkovo)", "setting_notifications_total_progress_title": "Zobraziť celkový priebeh zálohovania na pozadí", "setting_video_viewer_looping_title": "Opakovanie", + "setting_video_viewer_original_video_subtitle": "Pri streamovaní videa zo servera prehrať originál, aj keď je k dispozícii prekódované video. Môže to viesť k prerušovanému prehrávaniu videa. Videá dostupné lokálne sa prehrajú v pôvodnej kvalite bez ohľadu na toto nastavenie.", + "setting_video_viewer_original_video_title": "Vynútiť pôvodné video", "settings": "Nastavenia", "settings_require_restart": "Na použitie tohto nastavenia reštartujte Immich", "settings_saved": "Nastavenia boli uložené", + "setup_pin_code": "Nastavte si PIN kód", "share": "Zdieľať", + "share_action_prompt": "{count} položiek zdieľaných", "share_add_photos": "Pridať fotografie", "share_assets_selected": "{count} označených", "share_dialog_preparing": "Pripravujem...", @@ -1574,14 +1734,14 @@ "shared_link_edit_password_hint": "Zadajte heslo zdieľania", "shared_link_edit_submit_button": "Aktualizovať odkaz", "shared_link_error_server_url_fetch": "Nemožno nájsť URL severa", - "shared_link_expires_day": "Vyprší o {count} dní", + "shared_link_expires_day": "Vyprší o {count} deň", "shared_link_expires_days": "Vyprší o {count} dní", - "shared_link_expires_hour": "Vyprší o {count} hodín", + "shared_link_expires_hour": "Vyprší o {count} hodinu", "shared_link_expires_hours": "Vyprší o {count} hodín", - "shared_link_expires_minute": "Vyprší o {count} minút", + "shared_link_expires_minute": "Vyprší o {count} minútu", "shared_link_expires_minutes": "Vyprší o {count} minút", "shared_link_expires_never": "Nevyprší", - "shared_link_expires_second": "Vyprší o {count} sekúnd", + "shared_link_expires_second": "Vyprší o {count} sekundu", "shared_link_expires_seconds": "Vyprší o {count} sekúnd", "shared_link_individual_shared": "Individuálne zdieľané", "shared_link_info_chip_metadata": "EXIF", @@ -1589,7 +1749,8 @@ "shared_link_options": "Možnosti zdieľaných odkazov", "shared_links": "Zdieľané odkazy", "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", - "shared_photos_and_videos_count": "{assetCount, plural, other {# zdieľané fotky a videá.}}", + "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", + "shared_with_me": "Zdieľané so mnou", "shared_with_partner": "Zdieľané s {partner}", "sharing": "Zdieľanie", "sharing_enter_password": "Ak chcete zobraziť túto stránku, prosím, zadajte heslo.", @@ -1599,7 +1760,7 @@ "sharing_sidebar_description": "Zobraziť odkaz na Zdieľanie v bočnom paneli", "sharing_silver_appbar_create_shared_album": "Vytvoriť zdieľaný album", "sharing_silver_appbar_share_partner": "Zdieľať s partnerom", - "shift_to_permanent_delete": "stlačte ⇧ pre nemenné zmazanie pložiek", + "shift_to_permanent_delete": "stlačte ⇧ na trvalé vymazanie položky", "show_album_options": "Zobraziť možnosti albumu", "show_albums": "Zobraziť albumy", "show_all_people": "Zobraziť všetkých ľudí", @@ -1608,21 +1769,21 @@ "show_gallery": "Zobraziť galériu", "show_hidden_people": "Zobraziť skrytých ľudí", "show_in_timeline": "Zobraziť na časovej osi", - "show_in_timeline_setting_description": "Zobrazí fotky a videá tohoto používateľa na časovej osi", + "show_in_timeline_setting_description": "Zobraziť fotky a videá tohoto používateľa na vašej časovej osi", "show_keyboard_shortcuts": "Zobraziť klávesové skratky", "show_metadata": "Zobraziť metadáta", - "show_or_hide_info": "Zobrazí alebo skryje info", + "show_or_hide_info": "Zobraziť alebo skryť informácie", "show_password": "Zobraziť heslo", - "show_person_options": "Zobrazí možnosti osoby", - "show_progress_bar": "Zobrazí ukazovateľ priebehu", + "show_person_options": "Zobraziť možnosti osoby", + "show_progress_bar": "Zobraziť ukazovateľ priebehu", "show_search_options": "Zobraziť možnosti vyhľadávania", "show_shared_links": "Zobraziť zdieľané odkazy", - "show_slideshow_transition": "Zobrazí prechody v prezentácii", + "show_slideshow_transition": "Zobraziť prechody v prezentácii", "show_supporter_badge": "Odznak podporovateľa", "show_supporter_badge_description": "Zobraziť odznak podporovateľa", "shuffle": "Náhodné poradie", "sidebar": "Bočný panel", - "sidebar_display_description": "Zobrazí odkaz na pohľad v bočnom paneli", + "sidebar_display_description": "Zobraziť odkaz na zobrazenie v bočnom paneli", "sign_out": "Odhlásiť sa", "sign_up": "Registrovať", "size": "Veľkosť", @@ -1641,15 +1802,17 @@ "sort_title": "Názov", "source": "Zdroj", "stack": "Zoskupenie", + "stack_action_prompt": "{count} zoskupených", "stack_duplicates": "Zoskupiť duplicity", "stack_select_one_photo": "Vyberte jednu hlavnú fotku pre zoskupenie", "stack_selected_photos": "Zoskupiť vybraté fotky", - "stacked_assets_count": "{count, plural, one {Zoskupená # položka} other {Zoskupených # položiek}}", + "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", "start_date": "Začiatočný dátum", "state": "Štát", "status": "Stav", + "stop_casting": "Zastaviť prenos", "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", @@ -1667,24 +1830,31 @@ "swap_merge_direction": "Vymeniť smer zlúčenia", "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", - "tag": "Značka", - "tag_assets": "Pridať značku", - "tag_created": "Vytvorená značka: {tag}", - "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických značiek", - "tag_not_found_question": "Neviete nájsť značku? Vytvorte novú značku.", + "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", + "tag": "Štítok", + "tag_assets": "Označiť položky", + "tag_created": "Vytvorený štítok: {tag}", + "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", + "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", "tag_people": "Označiť ľudí", - "tag_updated": "Upravená značka: {tag}", - "tagged_assets": "Značka priradená {count, plural, one {# položke} other {# položkám}}", + "tag_updated": "Upravený štítok: {tag}", + "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", - "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových preferencií v prehliadači", - "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach položiek", + "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových predvolieb v prehliadači", + "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach médií", "theme_setting_asset_list_tiles_per_row_title": "Počet položiek na riadok ({count})", - "theme_setting_image_viewer_quality_subtitle": "Prispôsobenie kvality prehliadača detailov", + "theme_setting_colorful_interface_subtitle": "Použiť základnú farbu na plochy na pozadí.", + "theme_setting_colorful_interface_title": "Farebné rozhranie", + "theme_setting_image_viewer_quality_subtitle": "Upravte kvalitu detailného prehliadača obrázkov", "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrázkov", - "theme_setting_system_theme_switch": "Automaticky (podľa systemového nastavenia)", + "theme_setting_primary_color_subtitle": "Vyberte si farbu pre základné akcie a dôrazy.", + "theme_setting_primary_color_title": "Základná farba", + "theme_setting_system_primary_color_title": "Použiť systémovú farbu", + "theme_setting_system_theme_switch": "Automaticky (podľa systémového nastavenia)", "theme_setting_theme_subtitle": "Vyberte nastavenia témy aplikácie", "theme_setting_three_stage_loading_subtitle": "Trojstupňové načítanie môže zvýšiť výkonnosť načítania, ale vedie k výrazne vyššiemu zaťaženiu siete", "theme_setting_three_stage_loading_title": "Povolenie trojstupňového načítavania", @@ -1693,7 +1863,7 @@ "time_based_memories": "Časové spomienky", "timeline": "Časová os", "timezone": "Časové pásmo", - "to_archive": "Archivovať", + "to_archive": "Archív", "to_change_password": "Zmeniť heslo", "to_favorite": "Obľúbiť", "to_login": "Prihlásiť", @@ -1703,23 +1873,29 @@ "total": "Celkom", "total_usage": "Celkové využitie", "trash": "Kôš", + "trash_action_prompt": "{count} presunutých do koša", "trash_all": "Všetko do koša", "trash_count": "{count, number} do koša", "trash_delete_asset": "Položky do koša/odstrániť", + "trash_emptied": "Kôš vyprázdnený", "trash_no_results_message": "Vymazané fotografie a videá sa zobrazia tu.", "trash_page_delete_all": "Vymazať všetky", - "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z Immichu", + "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z aplikácie Immich", "trash_page_info": "Médiá v koši sa permanentne odstránia po {days} dňoch", "trash_page_no_assets": "Žiadne médiá v koši", "trash_page_restore_all": "Obnoviť všetky", - "trash_page_select_assets_btn": "Označiť médiá", + "trash_page_select_assets_btn": "Vybrať médiá", "trash_page_title": "Kôš ({count})", "trashed_items_will_be_permanently_deleted_after": "Položky v koši sa natrvalo vymažú po {days, plural, one {# dni} other {# dňoch}}.", "type": "Typ", + "unable_to_change_pin_code": "Nie je možné zmeniť PIN kód", + "unable_to_setup_pin_code": "Nie je možné nastaviť PIN kód", "unarchive": "Odarchivovať", + "unarchive_action_prompt": "{count} odstránené z archívu", "unarchived_count": "{count, plural, other {Odarchivovaných #}}", "undo": "Späť", "unfavorite": "Odznačiť ako obľúbené", + "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", "unknown_country": "Neznámy štát", @@ -1727,7 +1903,7 @@ "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", "unlink_oauth": "Odpojiť OAuth", - "unlinked_oauth_account": "Odpojiť OAuth účet", + "unlinked_oauth_account": "Odpojený OAuth účet", "unmute_memories": "Zrušenie stlmenia spomienok", "unnamed_album": "Nepomenovaný album", "unnamed_album_delete_confirmation": "Ste si istý, že chcete zmazať tento album?", @@ -1735,18 +1911,22 @@ "unsaved_change": "Neuložená zmena", "unselect_all": "Zrušiť výber všetkých", "unselect_all_duplicates": "Zrušiť výber všetkých duplicít", + "unselect_all_in": "Zrušiť výber všetkých v {group}", "unstack": "Odskupiť", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} nezoskupených", + "unstacked_assets_count": "Zrušenie zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}", + "untagged": "Bez štítku", "up_next": "To je všetko", "updated_at": "Aktualizované", "updated_password": "Heslo zmenené", "upload": "Nahrať", "upload_concurrency": "Súbežnosť nahrávania", + "upload_details": "Podrobnosti o nahrávaní", "upload_dialog_info": "Chcete zálohovať zvolené médiá na server?", "upload_dialog_title": "Nahrať médiá", - "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku aby sa zobrazili nové položky.", + "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku, aby sa zobrazili nové položky.", "upload_progress": "Ostáva {remaining, number} - Spracovaných {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicita} few {Preskočené # duplicity} other {Preskočených # duplicít}}", + "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicitná položka} few {Preskočené # duplicitné položky} other {Preskočených # duplicitných položiek}}", "upload_status_duplicates": "Duplikáty", "upload_status_errors": "Chyby", "upload_status_uploaded": "Nahrané", @@ -1755,6 +1935,8 @@ "uploading": "Nahrávanie", "url": "Odkaz URL", "usage": "Použitie", + "use_biometric": "Použiť biometrické údaje", + "use_current_connection": "použiť aktuálne pripojenie", "use_custom_date_range": "Použiť radšej vlastný rozsah dátumov", "user": "Používateľ", "user_has_been_deleted": "Tento používateľ bol vymazaný.", @@ -1762,16 +1944,19 @@ "user_liked": "Používateľovi {user} sa páči {type, select, photo {táto fotka} video {toto video} asset {táto položka} other {toto}}", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "Spravujte svoj PIN kód", + "user_privacy": "Ochrana osobných údajov používateľa", "user_purchase_settings": "Nákup", - "user_purchase_settings_description": "Správa vášho nákupu", + "user_purchase_settings_description": "Spravujte svoj nákup", "user_role_set": "Nastav {user} ako {role}", "user_usage_detail": "Podrobnosti o využívaní používateľmi", "user_usage_stats": "Štatistiky využitia účtu", "user_usage_stats_description": "Zobraziť štatistiky využitia účtu", "username": "Používateľské meno", "users": "Používatelia", + "users_added_to_album_count": "{count, plural, one {Pridaný # používateľ} few {Pridaní # používatelia} other {Pridaných # používateľov}} do albumu", "utilities": "Nástroje", "validate": "Validovať", + "validate_endpoint_error": "Zadajte prosím platnú URL adresu", "variables": "Premenné", "version": "Verzia", "version_announcement_closing": "Tvoj kamarát, Alex", @@ -1783,10 +1968,11 @@ "video_hover_setting_description": "Prehrá video náhľad keď kurzor myši prejde cez položku. Aj keď je vypnuté, prehrávanie sa môže spustiť nabehnutí cez ikonu Prehrať.", "videos": "Videá", "videos_count": "{count, plural, one {# Video} few {# Videá} other {# Videí}}", - "view": "Zobraziť", + "view": "Zobrazenie", "view_album": "Zobraziť Album", "view_all": "Zobraziť všetky", "view_all_users": "Zobraziť všetkých používateľov", + "view_details": "Zobraziť podrobnosti", "view_in_timeline": "Zobraziť v časovej osi", "view_link": "Zobraziť odkaz", "view_links": "Zobraziť odkazy", @@ -1799,7 +1985,7 @@ "viewer_remove_from_stack": "Odstrániť zo zoskupenia", "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", - "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} other {# ľudí}}", + "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "waiting": "Čaká", "warning": "Varovanie", "week": "Týždeň", diff --git a/i18n/sl.json b/i18n/sl.json index 5132d09fc8..c4409c717e 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Upravljanje nastavitev metapodatkov", "migration_job": "Migracija", "migration_job_description": "Preselite sličice za sredstva in obraze v najnovejšo strukturo map", + "nightly_tasks_cluster_faces_setting_description": "Zaženi prepoznavanje obrazov na novo zaznanih obrazih", + "nightly_tasks_cluster_new_faces_setting": "Združite nove obraze", + "nightly_tasks_database_cleanup_setting": "Naloge čiščenja baze podatkov", + "nightly_tasks_database_cleanup_setting_description": "Očistite stare, potekle podatke iz baze podatkov", + "nightly_tasks_generate_memories_setting": "Ustvarjajte spomine", + "nightly_tasks_generate_memories_setting_description": "Ustvarite nove spomine iz sredstev", + "nightly_tasks_missing_thumbnails_setting": "Ustvari manjkajoče sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Sredstva brez sličic postavite v čakalno vrsto za ustvarjanje sličic", + "nightly_tasks_settings": "Nastavitve nočnih opravil", + "nightly_tasks_settings_description": "Upravljajte nočne naloge", + "nightly_tasks_start_time_setting": "Začetni čas", + "nightly_tasks_start_time_setting_description": "Čas, ko strežnik začne izvajati nočne naloge", + "nightly_tasks_sync_quota_usage_setting": "Poraba kvote za sinhronizacijo", + "nightly_tasks_sync_quota_usage_setting_description": "Posodobi kvoto shrambe uporabnikov glede na trenutno uporabo", "no_paths_added": "Ni dodanih poti", "no_pattern_added": "Brez dodanega vzorca", "note_apply_storage_label_previous_assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilni preusmeritveni URI", "oauth_mobile_redirect_uri_override": "Preglasitev URI preusmeritve za mobilne naprave", "oauth_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je ''{callback}''", + "oauth_role_claim": "Zahteva vloge", + "oauth_role_claim_description": "Samodejno dodeli skrbniški dostop na podlagi prisotnosti tega zahtevka. Zahtevek ima lahko »uporabnik« ali »skrbnik«.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", @@ -357,10 +373,12 @@ "admin_password": "Skrbniško geslo", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Preizkusite novo izkušnjo aplikacije", + "advanced_settings_beta_timeline_title": "Časovnica beta različice", "advanced_settings_enable_alternate_media_filter_subtitle": "Uporabite to možnost za filtriranje medijev med sinhronizacijo na podlagi alternativnih meril. To poskusite le, če imate težave z aplikacijo, ki zaznava vse albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Uporabite alternativni filter za sinhronizacijo albuma v napravi", "advanced_settings_log_level_title": "Nivo dnevnika: {level}", - "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz sredstev v napravi. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", + "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz lokalnih sredstev. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", "advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich pošlje ob vsaki mrežni zahtevi", "advanced_settings_proxy_headers_title": "Proxy glave", @@ -388,6 +406,7 @@ "album_options": "Možnosti albuma", "album_remove_user": "Odstrani uporabnika?", "album_remove_user_confirmation": "Ali ste prepričani, da želite odstraniti {user}?", + "album_search_not_found": "Ni najdenih albumov, ki bi ustrezali vašemu iskanju", "album_share_no_users": "Videti je, da ste ta album dali v skupno rabo z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi ga lahko delili.", "album_updated": "Album posodobljen", "album_updated_setting_description": "Prejmite e-poštno obvestilo, ko ima album v skupni rabi nova sredstva", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Privzeti vrstni red razvrščanja albumov", "albums_default_sort_order_description": "Začetni vrstni red razvrščanja sredstev pri ustvarjanju novih albumov.", "albums_feature_description": "Zbirke sredstev, ki jih je mogoče deliti z drugimi uporabniki.", + "albums_on_device_count": "Albumi v napravi ({count})", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -427,6 +447,7 @@ "app_settings": "Nastavitve aplikacije", "appears_in": "Pojavi se v", "archive": "Arhiv", + "archive_action_prompt": "v arhiv je dodanih {count}", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", "archive_page_title": "Arhiv ({count})", @@ -464,7 +485,6 @@ "assets": "Sredstva", "assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album", - "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v album", "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_deleted_permanently": "trajno izrisana sredstva {count}", @@ -587,6 +607,7 @@ "cancel": "Prekliči", "cancel_search": "Prekliči iskanje", "canceled": "Preklicano", + "canceling": "Preklic", "cannot_merge_people": "Oseb ni mogoče združiti", "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", @@ -703,7 +724,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", - "darkTheme": "Preklopi na temno temo", + "dark_theme": "Preklopi temno temo", "date_after": "Datum po", "date_and_time": "Datum in ura", "date_before": "Datum pred", @@ -719,6 +740,7 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in številke glede na lokalne nastavitve brskalnika", "delete": "Izbriši", + "delete_action_prompt": "trajno izbrisano {count}", "delete_album": "Izbriši album", "delete_api_key_prompt": "Ali ste prepričani, da želite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaše naprave", @@ -732,6 +754,7 @@ "delete_key": "Izbriši ključ", "delete_library": "Izbriši knjižnico", "delete_link": "Izbriši povezavo", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriši", "delete_others": "Izbriši ostale", @@ -745,6 +768,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", + "deselect_all": "Prekliči vse", "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "Onemogočeno", @@ -762,6 +786,7 @@ "documentation": "Dokumentacija", "done": "Končano", "download": "Prenesi", + "download_action_prompt": "Prenašanje {count} sredstev", "download_canceled": "Prenos preklican", "download_complete": "Prenos končan", "download_enqueue": "Prenos v čakalni vrsti", @@ -799,6 +824,7 @@ "edit_key": "Uredi ključ", "edit_link": "Uredi povezavo", "edit_location": "Uredi lokacijo", + "edit_location_action_prompt": "urejenih {count} lokacij", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi osebe", @@ -817,6 +843,7 @@ "empty_trash": "Izprazni smetnjak", "empty_trash_confirmation": "Ste prepričani, da želite izprazniti smetnjak? S tem boste iz Immicha trajno odstranili vsa sredstva v smetnjaku.\nTega dejanja ne morete razveljaviti!", "enable": "Omogoči", + "enable_backup": "Omogoči varnostno kopiranje", "enable_biometric_auth_description": "Vnesite svojo PIN kodo, da omogočite biometrično preverjanje pristnosti", "enabled": "Omogočeno", "end_date": "Končni datum", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Sredstev ni bilo mogoče naložiti", "failed_to_load_folder": "Mape ni bilo mogoče naložiti", "favorite": "Priljubljen", + "favorite_action_prompt": "med priljubljene je dodanih {count}", "favorite_or_unfavorite_photo": "Priljubljena ali nepriljubljena fotografija", "favorites": "Priljubljene", "favorites_page_no_favorites": "Ni priljubljenih sredstev", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Nazadnje ustvarjeno", "library_page_sort_last_modified": "Nazadnje spremenjeno", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svetlo", "like_deleted": "Všeček izbrisan", "link_motion_video": "Povezava videa gibanja", @@ -1246,6 +1275,7 @@ "more": "Več", "move": "Premakni", "move_off_locked_folder": "Premakni iz zaklenjene mape", + "move_to_lock_folder_action_prompt": "V zaklenjeno mapo je bilo dodanih {count}", "move_to_locked_folder": "Premakni v zaklenjeno mapo", "move_to_locked_folder_confirmation": "Te fotografije in videoposnetki bodo odstranjeni iz vseh albumov in si jih bo mogoče ogledati le v zaklenjeni mapi", "moved_to_archive": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v arhiv", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Status podpornika", "purchase_server_title": "Strežnik", "purchase_settings_server_activated": "Ključ izdelka strežnika upravlja skrbnik", + "queue_status": "Čakalna vrsta {count}/{total}", "rating": "Ocena z zvezdicami", "rating_clear": "Počisti oceno", "rating_count": "{count, plural, one {# zvezdica} two {# zvezdici} few {# zvezdice} other {# zvezdic}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Odstrani časovno obdobje po meri", "remove_deleted_assets": "Odstrani izbrisana sredstva", "remove_from_album": "Odstrani iz albuma", + "remove_from_album_action_prompt": "{count} odstranjenih iz albuma", "remove_from_favorites": "Odstrani iz priljubljenih", + "remove_from_lock_folder_action_prompt": "iz zaklenjene mape je odstranjenih {count}", "remove_from_locked_folder": "Odstrani iz zaklenjene mape", "remove_from_locked_folder_confirmation": "Ali ste prepričani, da želite premakniti te fotografije in videoposnetke iz zaklenjene mape? Vidni bodo v vaši knjižnici.", "remove_from_shared_link": "Odstrani iz skupne povezave", @@ -1667,6 +1700,7 @@ "settings_saved": "Nastavitve shranjene", "setup_pin_code": "Nastavi PIN kodo", "share": "Deli", + "share_action_prompt": "Deljena sredstva {count}", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} izbrano", "share_dialog_preparing": "Priprava...", @@ -1768,6 +1802,7 @@ "sort_title": "Naslov", "source": "Vir", "stack": "Sklad", + "stack_action_prompt": "{count} naloženih", "stack_duplicates": "Nabor dvojnikov", "stack_select_one_photo": "Izberite eno glavno fotografijo za nabor", "stack_selected_photos": "Nabor izbranih fotografij", @@ -1838,6 +1873,7 @@ "total": "Skupno", "total_usage": "Skupna poraba", "trash": "Smetnjak", + "trash_action_prompt": "premaknjeno v smetnjak {count}", "trash_all": "Vse v smetnjak", "trash_count": "Smetnjak {count, number}", "trash_delete_asset": "V smetnjak/izbriši sredstvo", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "PIN kode ni mogoče spremeniti", "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", + "unarchive_action_prompt": "{count} odstranjenih iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", "undo": "Razveljavi", "unfavorite": "Odznači priljubljeno", + "unfavorite_action_prompt": "{count} odstranjenih iz priljubljenih", "unhide_person": "Prikaži osebo", "unknown": "Neznano", "unknown_country": "Neznana država", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Odznači vse dvojnike", "unselect_all_in": "Prekliči izbor vseh v {group}", "unstack": "Razklad", + "unstack_action_prompt": "{count} razloženih", "unstacked_assets_count": "Razloži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "untagged": "Neoznačeno", "up_next": "Naslednja", "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "Naloži", "upload_concurrency": "Sočasnost nalaganja", + "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali želite varnostno kopirati izbrana sredstva na strežnik?", "upload_dialog_title": "Naloži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osvežite stran, da vidite nova sredstva za nalaganje.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Oglejte si statistiko uporabe računa", "username": "Uporabniško ime", "users": "Uporabniki", + "users_added_to_album_count": "V album {count, plural, one {je bil dodan # uporabnik} two {sta bila dodana # uporabnika} few {so bili dodani # uporabniki} other {je bilo dodanih # uporanikov}}", "utilities": "Pripomočki", "validate": "Potrdi", "validate_endpoint_error": "Vnesite veljaven URL", @@ -1930,6 +1972,7 @@ "view_album": "Ogled albuma", "view_all": "Poglej vse", "view_all_users": "Ogled vseh uporabnikov", + "view_details": "Ogled podrobnosti", "view_in_timeline": "Ogled na časovnici", "view_link": "Odpri povezavo", "view_links": "Ogled povezav", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index b518bb39af..6216e671a8 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -458,7 +458,6 @@ "assets": "Записи", "assets_added_count": "Додато {count, plural, one {# датотека} other {# датотека}}", "assets_added_to_album_count": "Додато је {count, plural, one {# датотека} other {# датотека}} у албум", - "assets_added_to_name_count": "Додато {count, plural, one {# датотека} other {# датотеке}} у {hasName, select, true {{name}} other {нови албум}}", "assets_count": "{count, plural, one {# датотека} few {# датотеке} other {# датотека}}", "assets_deleted_permanently": "{count} елемената трајно обрисано", "assets_deleted_permanently_from_server": "{count} ресурс(а) трајно обрисан(а) са Immich сервера", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index aa92e6da49..a757146861 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -456,7 +456,6 @@ "assets": "Zapisi", "assets_added_count": "Dodato {count, plural, one {# datoteka} other {# datoteka}}", "assets_added_to_album_count": "Dodato je {count, plural, one {# datoteka} other {# datoteka}} u album", - "assets_added_to_name_count": "Dodato {count, plural, one {# datoteka} other {# datoteke}} u {hasName, select, true {{name}} other {novi album}}", "assets_count": "{count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", "assets_deleted_permanently": "{count} elemenata trajno obrisano", "assets_deleted_permanently_from_server": "{count} resurs(a) trajno obrisan(a) sa Immich servera", diff --git a/i18n/sv.json b/i18n/sv.json index a4c08f8c22..00f5d95b66 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -22,6 +22,7 @@ "add_partner": "Lägg till partner", "add_path": "Lägg till sökväg", "add_photos": "Lägg till foton", + "add_tag": "Lägg till tagg", "add_to": "Lägg till i…", "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} tillagda till favoriter", "admin": { "add_exclusion_pattern_description": "Lägg till exkluderande mönster. Matchning med jokertecken *, ** samt ? stödjs. För att ignorera alla filer i samtliga mappar som heter \"Raw\", använd \"**/Raw/**\". För att ignorera alla filer som slutar med \".tif\", använd \"**/*.tif\". För att ignorera en absolut sökväg, använd \"/sökväg/att/ignorera/**\".", + "admin_user": "Adminanvändare", "asset_offline_description": "Denna externa bibliotekstillgång finns inte längre på disken och har flyttats till papperskorgen. Om filen flyttades inom biblioteket, kontrollera din tidslinje för den nya motsvarande tillgången. För att återställa denna tillgång, se till att filsökvägen nedan kan nås av Immich och skanna biblioteket.", "authentication_settings": "Autentiseringsinställningar", "authentication_settings_description": "Hantera lösenord, OAuth, och andra autentiseringsinställningar", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten på tidigare uppladdade tillgångar kör du", "note_cannot_be_changed_later": "OBS: Detta kan inte ändras i efterhand!", "notification_email_from_address": "Från adress", - "notification_email_from_address_description": "Avsändarens epost, t.ex.: \"Immich Fotoserver \"", + "notification_email_from_address_description": "Avsändarens e-post, till exempel: \"Immich Fotoserver \". Säkerställ att du använder en adress som du har tillåtelse att skicka e-post från.", "notification_email_host_description": "Värd för epostservern (t.ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorera certifikatfel", "notification_email_ignore_certificate_errors_description": "Ignorera valideringsfel för TLS-certifikat (rekommenderas ej)", @@ -194,6 +196,8 @@ "oauth_mobile_redirect_uri": "Telefonomdirigernings-URI", "oauth_mobile_redirect_uri_override": "Telefonomdirigerings-URI överrskridning", "oauth_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantören inte tillåter mobila URI:er, så som ''{callback}''", + "oauth_role_claim": "Rollanspråk", + "oauth_role_claim_description": "Bevilja administratörsåtkomst automatiskt baserat på förekomsten av detta påstående. Påståendet kan innehålla antingen 'user' eller 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "För ytterligare detaljer om denna funktion, se dokumentationen.", @@ -202,7 +206,7 @@ "oauth_storage_quota_claim": "Användaranknuten lagringskvot", "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", - "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 för obegränsad kvot).", + "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts.", "oauth_timeout": "Begäran tog för lång tid", "oauth_timeout_description": "Timeout för förfrågningar i millisekunder", "password_enable_description": "Logga in med epost och lösenord", @@ -242,6 +246,7 @@ "storage_template_migration_info": "Lagringsmallen kommer konvertera alla filändelser till gemena bokstäver. Ändringar gäller endast för nya resurser, för att retoaktivt tillämpa mallen på befintliga resurser kör {job}.", "storage_template_migration_job": "Lagringsmall migreringsjobb", "storage_template_more_details": "För mer information om den här funktionen se Lagringsmall och dess konsekvenser", + "storage_template_onboarding_description_v2": "Denna funktion kommer när den är aktiverad att auto-organisera filer baserat på en användardefinierad mall. För mer information se dokumentationen.", "storage_template_path_length": "Uppskattad längdbegränsning på sökväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "Hantera mappstruktur och filnamn för uppladdade resurser", @@ -401,6 +406,9 @@ "album_with_link_access": "Låt alla med länken se foton och personer i det här albumet.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Standard sorteringsordning för album", + "albums_default_sort_order_description": "Standard sorteringsordning för mediefiler vid skapande av nytt album.", + "albums_feature_description": "Samlingar av mediefiler som kan delas med andra användare.", "all": "Allt", "all_albums": "Alla album", "all_people": "Alla personer", @@ -421,6 +429,7 @@ "app_settings": "Appinställningar", "appears_in": "Visas i", "archive": "Arkiv", + "archive_action_prompt": "{count} adderade till Arkiv", "archive_or_unarchive_photo": "Arkivera eller oarkivera fotot", "archive_page_no_archived_assets": "Inga arkiverade objekt hittade", "archive_page_title": "Arkiv ({count})", @@ -458,7 +467,6 @@ "assets": "Objekt", "assets_added_count": "La till {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet", - "assets_added_to_name_count": "Lade till {count, plural, one {# objekt} other {# objekt}} till {hasName, select, true {{name}} other {nytt album}}", "assets_count": "{count, plural, one {# objekt} other {# objekt}}", "assets_deleted_permanently": "{count} objekt har tagits bort permanent", "assets_deleted_permanently_from_server": "{count} objekt har tagits bort permanent från Immich-servern", @@ -639,6 +647,7 @@ "confirm_password": "Bekräfta lösenord", "confirm_tag_face": "Vill du tagga det här ansiktet som {name}?", "confirm_tag_face_unnamed": "Vill du tagga detta ansikte?", + "connected_device": "Ansluten enhet", "connected_to": "Ansluten till", "contain": "Anpassa", "context": "Sammanhang", @@ -691,7 +700,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mörk", - "darkTheme": "Växla till mörkt tema", "date_after": "Datum efter", "date_and_time": "Datum och Tid", "date_before": "Datum före", @@ -707,6 +715,7 @@ "default_locale": "Standardplats", "default_locale_description": "Formatera datum och siffror baserat på din webbläsares språkversion", "delete": "Radera", + "delete_action_prompt": "{count, plural, one {# permanent raderad} other {# permanent raderade}}", "delete_album": "Ta bort album", "delete_api_key_prompt": "Är du säker på att du vill ta bort denna API-nyckel?", "delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet", @@ -739,6 +748,7 @@ "disallow_edits": "Tillåt inte redigeringar", "discord": "Discord", "discover": "Upptäck", + "discovered_devices": "Funna enheter", "dismiss_all_errors": "Avvisa alla fel", "dismiss_error": "Avvisa fel", "display_options": "Visningsalternativ", @@ -1083,6 +1093,7 @@ "invite_to_album": "Bjuder in till album", "ios_debug_info_last_sync_at": "Senaste synkning {dateTime}", "ios_debug_info_no_processes_queued": "Inga bakgrundsprocesser köade", + "ios_debug_info_processes_queued": "{count, plural, one {{count} bakgrundsprocess köad} other {{count} bakgrundsprocesser köade}}", "items_count": "{count, plural, one {# objekt} other {# objekt}}", "jobs": "Jobb", "keep": "Behåll", @@ -1091,6 +1102,8 @@ "kept_this_deleted_others": "Behåll denna tillgång och borttagna {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Kortkommandon", "language": "Språk", + "language_no_results_title": "Inga språk funna", + "language_search_hint": "Sök språk…", "language_setting_description": "Välj önskat språk", "last_seen": "Senast sedd", "latest_version": "Senaste versionen", @@ -1129,6 +1142,7 @@ "locked_folder": "Låst Mapp", "log_out": "Logga ut", "log_out_all_devices": "Logga ut alla enheter", + "logged_in_as": "Inloggad som {user}", "logged_out_all_devices": "Loggat ut från alla enheter", "logged_out_device": "Loggat ut enheten", "login": "Logga in", @@ -1224,6 +1238,7 @@ "more": "Mer", "move": "Flytta", "move_off_locked_folder": "Flytta från låst mapp", + "move_to_lock_folder_action_prompt": "{count} adderades till låst mapp", "move_to_locked_folder": "Flytta till låst mapp", "move_to_locked_folder_confirmation": "Dessa foton och videor kommer tas bort från alla album och går endast se i låsta mappen", "moved_to_archive": "Flyttade {count, plural, one {# resurs} other {# assets}} till arkivet", @@ -1825,6 +1840,7 @@ "unable_to_setup_pin_code": "Kunde inte konfigurera pinkod", "unarchive": "Ångra arkivering", "unarchived_count": "{count, plural, one {# borttagen från arkiv} other {# borttagna från arkiv}}", + "undo": "Ångra", "unfavorite": "Avfavorisera", "unhide_person": "Visa person", "unknown": "Okänd", diff --git a/i18n/ta.json b/i18n/ta.json index ce0768982d..a91ea9d045 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -392,7 +392,6 @@ "assets": "சொத்துக்கள்", "assets_added_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_added_to_album_count": "ஆல்பத்தில் {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", - "assets_added_to_name_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}} {hasname, தேர்ந்தெடுக்கவும், உண்மை { {name} } பிற {new album}}", "assets_count": "{எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_moved_to_trash_count": "நகர்த்தப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}}", "assets_permanently_deleted_count": "நிரந்தரமாக நீக்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} பிற {# சொத்துக்கள்}}", diff --git a/i18n/te.json b/i18n/te.json index 8bd57bc3bc..4a2e938c9a 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -23,6 +23,8 @@ "add_photos": "ఫోటోలను జోడించండి", "add_to": "జోడించండి…", "add_to_album": "ఆల్బమ్‌కు జోడించండి", + "add_to_album_bottom_sheet_added": "ఆల్బమ్కు జోడించబడింది", + "add_to_album_bottom_sheet_already_exists": "ఆల్బమ్‌లో ఇప్పటికే జోడించబడింది", "add_to_shared_album": "భాగస్వామ్య ఆల్బమ్‌కు జోడించండి", "add_url": "URLని జోడించండి", "added_to_archive": "ఆర్కైవ్‌కి జోడించబడింది", @@ -30,17 +32,18 @@ "added_to_favorites_count": "ఇష్టమైన వాటికి {count, number} జోడించబడింది", "admin": { "add_exclusion_pattern_description": "మినహాయింపు నమూనాలను జోడించండి. *, ** మరియు ?ని ఉపయోగించి గ్లోబింగ్‌కు మద్దతు ఉంది. \"Raw\" అనే పేరు గల ఏదైనా డైరెక్టరీలోని అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/Raw/**\"ని ఉపయోగించండి. \".tif\"తో ముగిసే అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/*.tif\"ని ఉపయోగించండి. సంపూర్ణ మార్గాన్ని విస్మరించడానికి, \"/path/to/ignore/**\"ని ఉపయోగించండి.", + "admin_user": "నిర్వాహకుడు", "asset_offline_description": "ఈ బాహ్య లైబ్రరీ ఫైల్ ఇకపై డిస్క్‌లో కనుగొనబడలేదు మరియు ట్రాష్‌కు తరలించబడింది. ఫైల్ లైబ్రరీలోకి తరలించబడితే, కొత్త సంబంధిత ఫైల్ కోసం మీ టైమ్‌లైన్‌ను తనిఖీ చేయండి. ఈ ఫైల్ని పునరుద్ధరించడానికి, దయచేసి దిగువన ఉన్న ఫైల్ పాత్‌ను Immich యాక్సెస్ చేయగలదని నిర్ధారించుకోండి మరియు లైబ్రరీని స్కాన్ చేయండి.", "authentication_settings": "ప్రమాణీకరణ సెట్టింగ్‌లు", "authentication_settings_description": "పాస్‌వర్డ్, OAuth మరియు ఇతర ప్రమాణీకరణ సెట్టింగ్‌లను నిర్వహించండి", "authentication_settings_disable_all": "మీరు ఖచ్చితంగా అన్ని లాగిన్ పద్ధతులను నిలిపివేయాలనుకుంటున్నారా? లాగిన్ పూర్తిగా నిలిపివేయబడుతుంది.", "authentication_settings_reenable": "మళ్లీ ప్రారంబించటానికి, Server Commandని ఉపయోగించండి.", "background_task_job": "నేపథ్య పనులు", - "backup_database": "బ్యాకప్ డేటాబేస్", - "backup_database_enable_description": "డేటాబేస్ బ్యాకప్‌లను ప్రారంభించండి", - "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి బ్యాకప్‌ల మొత్తం", - "backup_settings": "బ్యాకప్ సెట్టింగ్‌లు", - "backup_settings_description": "డేటాబేస్ బ్యాకప్ సెట్టింగ్‌లను నిర్వహించండి", + "backup_database": "డేటాబేస్ ను సృష్టించు", + "backup_database_enable_description": "డేటాబేస్ పడవెయ్యడాన్నీ ప్రారంభించండి", + "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి పడవెయ్యడాల్లా మొత్తం", + "backup_settings": "డేటాబేస్ పడవెసే సెట్టింగ్‌లు", + "backup_settings_description": "డేటాబేస్ పడవెసే సెట్టింగ్‌లను నిర్వహించండి", "cleared_jobs": "దీని కోసం ఉద్యోగాలు క్లియర్ చేయబడ్డాయి: {job}", "config_set_by_file": "కాన్ఫిగరేషన్ ప్రస్తుతం కాన్ఫిగరేషన్ ఫైల్ ద్వారా సెట్ చేయబడింది", "confirm_delete_library": "మీరు ఖచ్చితంగా {library} లైబ్రరీని తొలగించాలనుకుంటున్నారా?", @@ -48,6 +51,7 @@ "confirm_email_below": "నిర్ధారించడానికి, క్రింద \"{email}\" టైప్ చేయండి", "confirm_reprocess_all_faces": "మీరు ఖచ్చితంగా అన్ని ముఖాలను రీప్రాసెస్ చేయాలనుకుంటున్నారా? ఇది పేరున్న వ్యక్తులను కూడా క్లియర్ చేస్తుంది.", "confirm_user_password_reset": "మీరు ఖచ్చితంగా {user} పాస్‌వర్డ్‌ని రీసెట్ చేయాలనుకుంటున్నారా?", + "confirm_user_pin_code_reset": "మీరు ఖచ్చితంగా {user} యొక్క పిన్ కోడ్ నీ రీసెట్ చేద్దామనిఅనుకుంటున్నారా?", "create_job": "పనిని సృష్టించండి", "cron_expression": "క్రాన్ వ్యక్తీకరణ", "cron_expression_description": "క్రాన్ ఫార్మాట్ ఉపయోగించి స్కానింగ్ విరామాన్ని సెట్ చేయండి. మరిన్ని వివరాల కోసం దయచేసి ఉదా. క్రోంటాబ్ గురు చూడండి", @@ -402,7 +406,6 @@ "assets": "ఆస్తులు", "assets_added_count": "జోడించబడినవి {count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_added_to_album_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} ఆల్బమ్‌కి జోడించబడినవి", - "assets_added_to_name_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} {hasName, select, true {{name}} other {కొత్త ఆల్బమ్}}కి జోడించబడినవి", "assets_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_moved_to_trash_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} చెత్తబుట్టలోకి తరలించారు", "assets_permanently_deleted_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} శాశ్వతంగా తొలగించబడినవి", diff --git a/i18n/th.json b/i18n/th.json index 13d9514d63..3cce9afd1c 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,14 +14,15 @@ "add_a_location": "เพิ่มตำแหน่ง", "add_a_name": "เพิ่มชื่อ", "add_a_title": "เพิ่มหัวข้อ", - "add_endpoint": "เพิ่มอุปกรณ์ปลายทาง", + "add_endpoint": "เพิ่มปลายทาง", "add_exclusion_pattern": "เพิ่มข้อยกเว้น", "add_import_path": "เพิ่มเส้นทางนำเข้า", "add_location": "เพิ่มตำแหน่ง", "add_more_users": "เพิ่มผู้ใช้งาน", - "add_partner": "เพิ่มพันธมิตร", + "add_partner": "เพิ่มคู่หู", "add_path": "เพิ่มพาทที่ตั้ง", "add_photos": "เพิ่มรูปภาพ", + "add_tag": "เพิ่มแท็ก", "add_to": "เพิ่มไปยัง …", "add_to_album": "เพิ่มไปอัลบั้ม", "add_to_album_bottom_sheet_added": "เพิ่มไปยัง {album}", @@ -33,7 +34,8 @@ "added_to_favorites_count": "{count, number} รูปถูกเพิ่มเข้ารายการโปรด", "admin": { "add_exclusion_pattern_description": "เพิ่มรูปแบบข้อยกเว้น รองรับการใช้ *, ** และ ? หากต้องการละเว้นไฟล์ทั้งหมดในไดเร็กทอรีที่ชื่อว่า \"Raw\" ให้ใช้ \"**/Raw/**\" ถ้าต้องการละเว้นไฟล์ทั้งหมดที่ลงท้ายด้วย \".tif\" ให้ใช้ \"**/*.tif\" ถ้าต้องการละเว้นพาธที่เริ่มจากไดเรกทอรีบนสุดให้ใช้ \"/พาธ/ที่ต้องการ/ละเว้น/**\"", - "asset_offline_description": "ไฟล์ Asset ของไลบรารีภายนอกนี้ไม่พบในดิสก์แล้ว และถูกย้ายไปที่ถังขยะ หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาแอสเซ็ตที่เกี่ยวข้องใหม่ หากต้องการกู้คืน Asset นี้ โปรดตรวจสอบให้แน่ใจว่า Immich สามารถเข้าถึงเส้นทางไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", + "admin_user": "ผู้ดูแล", + "asset_offline_description": "ไม่พบไฟล์สื่อของไลบรารีภายนอกนี้ในดิสก์ และถูกย้ายไปที่ถังขยะแล้ว หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาสื่อที่เกี่ยวข้องใหม่ หากต้องการกู้คืนสื่อนี้ โปรดตรวจสอบว่า Immich สามารถเข้าถึงไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", "authentication_settings": "การตั้งค่าการเข้าถึง", "authentication_settings_description": "จัดการรหัสผ่าน, OAuth, และตั้งค่าการเข้าถึงอื่นๆ", "authentication_settings_disable_all": "คุณแน่ใจว่าต้องการปิดวิธีการล็อกอินทั้งหมดหรือไม่? ล็อกอินจะถูกปิดทั้งหมด", @@ -43,7 +45,7 @@ "backup_database_enable_description": "เปิดใช้งานสำรองฐานข้อมูล", "backup_keep_last_amount": "จำนวนข้อมูลสำรองก่อนหน้าที่ต้องเก็บไว้", "backup_settings": "ตั้งค่าการสำรองข้อมูล", - "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล งานสำรองนี้จะไม่ถูกตรวจสอบและคุณจะไม่ได้รับการแจ้งเมื่อมันสำรองไม่สำเร็จ", + "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล", "cleared_jobs": "เคลียร์งานสำหรับ: {job}", "config_set_by_file": "การตั้งค่าคอนฟิกกำลังถูกกำหนดโดยไฟล์คอนฟิก", "confirm_delete_library": "คุณแน่ใจว่าอยากลบคลังภาพ {library} หรือไม่?", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "หากต้องการใช้ Storage Label กับไฟล์ที่อัปโหลดก่อนหน้านี้ ให้รันคำสั่งนี้", "note_cannot_be_changed_later": "หมายเหตุ: ไม่สามารถเปลี่ยนภายหลังได้!", "notification_email_from_address": "จากที่อยู่", - "notification_email_from_address_description": "อีเมลผู้ส่ง อย่างเช่น \"Immich Photo Server \"", + "notification_email_from_address_description": "ที่อยู่อีเมลผู้ส่ง ตัวอย่าง \"Immich Photo Server \" (กรุณายีนยันว่าสามารถส่งเมลจากที่อยู่นี้ได้)", "notification_email_host_description": "ที่อยู่เซิร์ฟเวอร์อีเมล (เช่น smtp.immich.app)", "notification_email_ignore_certificate_errors": "ไม่สนใจข้อผิดพลาดเกี่ยวกับใบรับรอง", "notification_email_ignore_certificate_errors_description": "ไม่สนใจการยืนยันใบรับรอง TLS ผิดพลาด (ไม่แนะนำ)", @@ -193,7 +195,7 @@ "oauth_enable_description": "ล็อกอินผ่าน OAuth", "oauth_mobile_redirect_uri": "URI เปลี่ยนเส้นทางบนโทรศัพท์", "oauth_mobile_redirect_uri_override": "แทนที่ URI เปลี่ยนเส้นทางบนโทรศัพท์", - "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อ 'app.immich:/' เป็น URI ที่เปลี่ยนเส้นทางไม่ถูกต้อง", + "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อผู้ให้บริการ OAuth ไม่อนุญาต URI เช่น \"{callback}\"", "oauth_settings": "OAuth", "oauth_settings_description": "จัดการการตั้งค่าล็อกอินผ่าน OAuth", "oauth_settings_more_details": "สำหรับรายละเอียดเพิ่มเติม ให้อ้างถึงเอกสาร", @@ -202,7 +204,7 @@ "oauth_storage_quota_claim": "สิทธิ์ที่ใช้อ้างถึงโควต้าพื้นที่จัดเก็บ", "oauth_storage_quota_claim_description": "ตั้งโควต้าพื้นที่จัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ", "oauth_storage_quota_default": "โควต้าพื้นที่เก็บข้อมูลเริ่มต้น (GiB)", - "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์ (ป้อน 0 สำหรับโควต้าไม่จำกัด)", + "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์", "oauth_timeout": "หมดเวลาการร้องขอ", "oauth_timeout_description": "ระยะเวลาหมดเวลาสำหรับการร้องขอ (หน่วยเป็นมิลลิวินาที)", "password_enable_description": "ล็อกอินกับอีเมลและรหัสผ่าน", @@ -242,6 +244,7 @@ "storage_template_migration_info": "เทมเพลตของการจัดเก็บข้อมูลจะเปลี่ยนตัวอักษรเป็นตัวพิมพ์เล็กทั้งหมด การเปลี่ยนแปลงเทมเพลตจะมีผลกับแอสเซ็ตใหม่เท่านั้น หากต้องการนำเทมเพลตไปใช้กับ Asset ที่อัปโหลดก่อนหน้านี้ ให้รัน {job}.", "storage_template_migration_job": "เทมเพลตการ Migration ข้อมูล", "storage_template_more_details": "สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ โปรดดูที่ Storage Template และ ผลกระทบ", + "storage_template_onboarding_description_v2": "เมื่อเปิด ฟีเจอร์จะจัดเก็บข้อมูลตามเทมเพลตที่ผู้ใช้กำหนด อ่านเพิ่มเติม", "storage_template_path_length": "ขีดจำกัดของความยาวพาธโดยประมาณ: {length, number}/{limit, number}", "storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล", "storage_template_settings_description": "จัดการโครงสร้างโฟลเดอร์และชื่อไฟล์ที่อัปโหลด", @@ -256,7 +259,7 @@ "template_email_update_album": "อัปเดตเทมเพลตอัลบั้ม", "template_email_welcome": "เทมเพลตสำหรับอีเมลต้อนรับ", "template_settings": "เทมเพลตการแจ้งเตือน", - "template_settings_description": "ปรับแต่งเทมเพลตแจ้งเตือน", + "template_settings_description": "ปรับแต่งเทมเพลตการแจ้งเตือน", "theme_custom_css_settings": "CSS กําหนดเอง", "theme_custom_css_settings_description": "Cascading Style Sheets ช่วยให้ปรับแต่งเค้าโครง Immich ได้", "theme_settings": "การตั้งค่าธีม", @@ -362,7 +365,7 @@ "advanced_settings_proxy_headers_subtitle": "กำหนด proxy headers ที่ Immich ควรส่งพร้อมกับแต่ละคำขอเครือข่าย", "advanced_settings_proxy_headers_title": "พ็อกซี่ เฮดเดอร์", "advanced_settings_self_signed_ssl_subtitle": "ข้ามการตรวจสอบใบรับรอง SSL จำเป็นสำหรับใบรับรองแบบ self-signed", - "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed ", + "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed", "advanced_settings_sync_remote_deletions_subtitle": "บหรือกู้คืนไฟล์บนอุปกรณ์นี้โดยอัตโนมัติเมื่อดำเนินการดังกล่าวผ่านเว็บ", "advanced_settings_sync_remote_deletions_title": "ซิงก์การลบจากระยะไกล [คุณสมบัติทดลอง]", "advanced_settings_tile_subtitle": "ตั้งค่าผู้ใช้งานขั้นสูง", @@ -401,6 +404,9 @@ "album_with_link_access": "อนุญาตให้ทุกคนที่มีลิงก์สามารถดูรูปภาพและผู้คนที่อยู่ในอัลบั้มนี้", "albums": "อัลบั้ม", "albums_count": "{count, plural, one {{count, number} อัลบั้ม} other {{count, number} อัลบั้ม}}", + "albums_default_sort_order": "การจัดเรียงอัลบั้มเริ่มต้น", + "albums_default_sort_order_description": "การจัดเรียงแอสเซ็ตเริ่มต้นเมื่อสร้างอัลบั้มใหม่", + "albums_feature_description": "กลุ่มของแอสเซ็ตที่สามารถส่งให้ผู้ใช้อื่นได้", "all": "ทั้งหมด", "all_albums": "อัลบั้มทั้งหมด", "all_people": "ทุกคน", @@ -413,8 +419,8 @@ "anti_clockwise": "ทวนเข็มนาฬิกา", "api_key": "API key", "api_key_description": "ค่านี้จะแสดงเพียงครั้งเดียว โปรดคัดลอกก่อนปิดหน้าต่าง", - "api_key_empty": "ชื่อคีย์ API ของคุณไม่ควรว่างเปล่า", - "api_keys": "API คีย์", + "api_key_empty": "ชื่อ API Key ของคุณไม่ควรว่างเปล่า", + "api_keys": "API Key", "app_bar_signout_dialog_content": "คุณแน่ใจว่าอยากออกจากระบบ", "app_bar_signout_dialog_ok": "ใช่", "app_bar_signout_dialog_title": "ออกจากระบบ", @@ -428,7 +434,7 @@ "archive_size_description": "ตั้งค่าขนาดสูงสุดสำหรับการดาวน์โหลด (GiB)", "archived": "เก็บถาวรแล้ว", "archived_count": "{count, plural, other {เก็บถาวร # รายการ}}", - "are_these_the_same_person": "เป็นคนเดียวกันหรือไม่?", + "are_these_the_same_person": "เป็นบุคคลเดียวกันหรือไม่?", "are_you_sure_to_do_this": "คุณแน่ใจว่าต้องการทำสิ่งนี้หรือไม่?", "asset_action_delete_err_read_only": "ไม่สามารถลบทรัพยากรแบบอ่านอย่างเดียวได้ กำลังข้าม", "asset_action_share_err_offline": "ไม่สามารถดึงข้อมูลทรัพยากรออฟไลน์ กำลังข้าม", @@ -448,24 +454,41 @@ "asset_list_settings_title": "ตารางรูปภาพ", "asset_offline": "สื่อออฟไลน์", "asset_offline_description": "ไม่พบทรัพยากรภายนอกนี้ในดิสก์อีกต่อไป โปรดติดต่อผู้ดูแลระบบ Immich ของคุณเพื่อขอความช่วยเหลือ", + "asset_restored_successfully": "กู้คืนสื่อสำเร็จ", "asset_skipped": "ข้ามแล้ว", "asset_skipped_in_trash": "ในถังขยะ", "asset_uploaded": "อัปโหลดแล้ว", "asset_uploading": "กำลังอัปโหลด…", + "asset_viewer_settings_subtitle": "ตั้งค่าการแสดงแกลเลอรี", "asset_viewer_settings_title": "ตัวดูทรัพยากร", "assets": "สื่อ", + "assets_added_count": "เพิ่ม {count, plural, one{# สื่อ} other {# สื่อ}} แล้ว", "assets_added_to_album_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยังอัลบั้ม", - "assets_added_to_name_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยัง {hasName, select, true {{name}} other {new album}}", + "assets_cannot_be_added_to_album_count": "ไม่สามารถเพิ่ม {count, plural, one {สื่อ} other {สื่อ}} ไปยังอัลบั้ม", + "assets_count": "{count, plural, one { สื่อ} other { สื่อ}}", + "assets_deleted_permanently": "{count} สื่อถูกลบอย่างถาวร", + "assets_deleted_permanently_from_server": "ลบ {count} สื่อออกจาก Immich อย่างถาวร", + "assets_downloaded_failed": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} ไม่สำเร็จ - {error}", + "assets_downloaded_successfully": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} สำเร็จ", "assets_moved_to_trash_count": "ย้าย {count, plural, one {# asset} other {# assets}} ไปยังถังขยะแล้ว", "assets_permanently_deleted_count": "ลบ {count, plural, one {# asset} other {# assets}} ทิ้งถาวร", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบแล้ว", + "assets_removed_permanently_from_device": "นำ {count} สื่อออกจากอุปกรณ์อย่างถาวร", "assets_restore_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนสื่อที่ทิ้งทั้งหมด? คุณไม่สามารถย้อนกลับการดำเนินการนี้ได้! โปรดทราบว่าสื่อออฟไลน์ใดๆ ไม่สามารถกู้คืนได้ด้วยวิธีนี้", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} คืนค่า", + "assets_restored_successfully": "กู้คืน {count} สื่อสำเร็จ", + "assets_trashed": "ย้าย {count} สื่อไปยังถังขยะ", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบ", + "assets_trashed_from_server": "ย้าย {count} สื่อจาก Immich ไปยังถังขยะ", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} อยู่ในอัลบั้มอยู่แล้ว", "authorized_devices": "อุปกรณ์ที่ได้รับอนุญาต", + "automatic_endpoint_switching_subtitle": "เชื่อมต่อด้วย LAN ภายในวง Wi-Fi ที่ระบุไว้ และเชื่อมต่อด้วยวิธีอื่นเมื่ออยู่นอก Wi-Fi ที่ระบุไว้", + "automatic_endpoint_switching_title": "สลับ URL อัตโนมัติ", + "autoplay_slideshow": "เล่นสไลด์โชว์", "back": "กลับ", "back_close_deselect": "ย้อนกลับ, ปิด, หรือยกเลิกการเลือก", + "background_location_permission": "การอนุญาตระบุตำแหน่งพื้นหลัง", + "background_location_permission_content": "เพื่อที่จะสลับการเชื่อมต่อขณะที่รันในพื้นหลัง Immich ต้องรู้ตำแหน่งที่แม่ยำตลอดเวลา เพื่อจะสามารถอ่านชื่อ Wi-Fi", "backup_album_selection_page_albums_device": "อัลบั้มบนเครื่อง ({count})", "backup_album_selection_page_albums_tap": "กดเพื่อรวม กดสองครั้งเพื่อยกเว้น", "backup_album_selection_page_assets_scatter": "ทรัพยาการสามารถกระจายไปในหลายอัลบั้ม ดังนั้นอัลบั้มสามารถถูกรวมหรือยกเว้นในกระบวนการสำรองข้อมูล", @@ -496,15 +519,16 @@ "backup_controller_page_background_is_on": "การสำรองข้อมูลอัตโนมัติเปิดอยู่", "backup_controller_page_background_turn_off": "ปิดบริการเบื้องหลัง", "backup_controller_page_background_turn_on": "เปิดบริการเบื้องหลัง", - "backup_controller_page_background_wifi": "บน WiFi เท่านั้น", + "backup_controller_page_background_wifi": "บน Wi-Fi เท่านั้น", "backup_controller_page_backup": "สำรองข้อมูล", "backup_controller_page_backup_selected": "ที่เลือก: ", "backup_controller_page_backup_sub": "รูปภาพและวิดีโอที่สำรองแล้ว", "backup_controller_page_created": "สร้างเมื่อ: {date}", "backup_controller_page_desc_backup": "เปิดการสำรองข้อมูลในฉากหน้าเพื่อที่จะอัพโหลดทรัพยากรใหม่ไปยังเซิร์ฟเวอร์เมื่อเปิดแอพ", - "backup_controller_page_excluded": "ถูกยกเว้น: ", + "backup_controller_page_excluded": "ยกเว้น: ", "backup_controller_page_failed": "ล้มเหลว ({count})", "backup_controller_page_filename": "ชื่อไฟล์: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "ข้อมูลเกี่ยวกับการสำรองข้อมูล", "backup_controller_page_none_selected": "ไม่มีที่เลือก", "backup_controller_page_remainder": "ที่เหลือ", @@ -526,7 +550,12 @@ "backup_manual_success": "สำเร็จ", "backup_manual_title": "สถานะอัพโหลด", "backup_options_page_title": "ตัวเลือกการสำรองข้อมูล", + "backup_setting_subtitle": "ตั้งค่าการอัพโหลดในฉากหน้า และพื้นหลัง", "backward": "กลับหลัง", + "biometric_auth_enabled": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกเปิด", + "biometric_locked_out": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกล็อค", + "biometric_no_options": "ไม่มีตัวเลือกการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", + "biometric_not_available": "ไม่สามารถใช้งานการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลได้บนอุปกรณ์นี้", "birthdate_saved": "บันทึกวันเกิดแล้ว", "birthdate_set_description": "วันที่เกิดจะนำมาใช้ในการคำนวณอายุของบุคคลนี้ในขณะที่ถ่ายรูป", "blurred_background": "พื้นหลังแบบเบลอ", @@ -556,14 +585,19 @@ "camera_model": "รุ่นกล้อง", "cancel": "ยกเลิก", "cancel_search": "ยกเลิกการค้นหา", + "canceled": "ยกเลิก", "cannot_merge_people": "ไม่สามารถรวมกลุ่มคนได้", "cannot_undo_this_action": "การกระทำนี้ไม่สามารถย้อนกลับได้!", "cannot_update_the_description": "ไม่สามารถอัพเดทรายละเอียดได้", + "cast": "แคสต์", + "cast_description": "ตั้งค่าปลายทางแคสต์", "change_date": "เปลี่ยนวันที่", + "change_description": "แก้ไขคำอธิบาย", + "change_display_order": "เปลี่ยนลำดับการแสดงผล", "change_expiration_time": "เปลี่ยนเวลาหมดอายุ", "change_location": "เปลี่ยนตําแหน่ง", "change_name": "เปลี่ยนชื่อ", - "change_name_successfully": "เปลี่ยนชื่อเรียบร้อยแล้ว", + "change_name_successfully": "เปลี่ยนชื่อสำเร็จ", "change_password": "เปลี่ยนรหัสผ่าน", "change_password_description": "การเข้าสู่ระบบครั้งแรก จำเป็นจต้องเปลี่ยนรหัสผ่านของคุณเพื่อความปลอดภัย โปรดป้อนรหัสผ่านใหม่ด้านล่าง", "change_password_form_confirm_password": "ยืนยันรหัสผ่าน", @@ -574,6 +608,9 @@ "change_pin_code": "เปลี่ยนรหัสประจำตัว (PIN)", "change_your_password": "เปลี่ยนรหัสผ่านของคุณ", "changed_visibility_successfully": "เปลี่ยนการมองเห็นเรียบร้อยแล้ว", + "check_corrupt_asset_backup": "ตรวจสอบสำรองสื่อที่ผิดปกติ", + "check_corrupt_asset_backup_button": "ตรวจสอบ", + "check_corrupt_asset_backup_description": "ตรวจสอบเมื่อเชื่อมต่อ Wi-Fi และสื่อทั้งหมดถูกสำรองข้อมูลแล้วเท่านั้น การตรวจสอบอาจใช้เวลาหลายนาที", "check_logs": "ตรวจสอบบันทึก", "choose_matching_people_to_merge": "เลือกคนที่ตรงกันเพื่อรวมเข้าด้วยกัน", "city": "เมือง", @@ -582,6 +619,14 @@ "clear_all_recent_searches": "ล้างประวัติการค้นหา", "clear_message": "ล้างข้อความ", "clear_value": "ล้างค่า", + "client_cert_dialog_msg_confirm": "เสร็จ", + "client_cert_enter_password": "ใส่รหัสผ่าน", + "client_cert_import": "นำเข้า", + "client_cert_import_success_msg": "นำเข้าใบรับรองสำเร็จ", + "client_cert_invalid_msg": "ใบรับรอง หรือรหัสผ่านไม่ถูกต้อง", + "client_cert_remove_msg": "ลบใบรับรองสำเร็จ", + "client_cert_subtitle": "รองรับเฉพาะ PKCS12 (.p12, .pfx) เท่านั้น การนำเข้า/ลบใบรับรองสามารถทำได้ก่อนล็อคอินเท่านั้น", + "client_cert_title": "ใบรับรอง SSL ไคลเอนต์", "clockwise": "ตามเข็มนาฬิกา", "close": "ปิด", "collapse": "ย่อ", @@ -602,6 +647,10 @@ "confirm_keep_this_delete_others": "จะลบทั้งหมดในรายการ และยกเว้นสื่อนี้หรือไม่ คุณแน่ใจใช่ไหมที่ต้องการดำเนินการต่อ?", "confirm_new_pin_code": "ยืนยันรหัสประจำตัว (PIN)", "confirm_password": "ยืนยันรหัสผ่าน", + "confirm_tag_face": "คุณต้องการแท็กใบหน้านี้ด้วยชื่อ {name} หรือไม่", + "confirm_tag_face_unnamed": "คุณต้องการแท็กใบหน้านี้หรือไม่", + "connected_device": "อุปกรณ์ที่เชื่อมต่อแล้ว", + "connected_to": "เชื่อมต่อไปยัง", "contain": "มีอยู่", "context": "บริบท", "continue": "ต่อไป", @@ -610,6 +659,7 @@ "control_bottom_app_bar_delete_from_local": "ลบจากเรื่อง", "control_bottom_app_bar_edit_location": "แก้ไขตำแหน่ง", "control_bottom_app_bar_edit_time": "แก้ไขวันและเวลา", + "control_bottom_app_bar_share_link": "แชร์ลิงค์", "control_bottom_app_bar_share_to": "แชร์ให้", "control_bottom_app_bar_trash_from_immich": "ย้ายเข้าถังขยะ", "copied_image_to_clipboard": "คัดลอกภาพไปยังคลิปบอร์ดแล้ว", @@ -631,6 +681,7 @@ "create_link": "สร้างลิงก์", "create_link_to_share": "สร้างลิงก์เพื่อแชร์", "create_link_to_share_description": "ผู้ที่มีลิงก์ สามารถดูรูปที่เลือกได้", + "create_new": "สร้างใหม่", "create_new_person": "สร้างคนใหม่", "create_new_person_hint": "กำหนดสื่อที่เลือกให้กับคนใหม่", "create_new_user": "สร้างผู้ใช้งานใหม่", @@ -640,9 +691,12 @@ "create_tag_description": "สร้างแท็กใหม่ สำหรับแท็กที่ซ้อนกัน โปรดป้อนเส้นทางทั้งหมดของแท็ก รวมถึงเครื่องหมายทับ", "create_user": "สร้างผู้ใช้", "created": "สร้างแล้ว", + "created_at": "สร้างเมื่อ", + "crop": "ครอป", "curated_object_page_title": "สิ่งของ", "current_device": "อุปกรณ์ปัจจุบัน", "current_pin_code": "รหัสประจำตัว (PIN) ปัจจุบัน", + "current_server_address": "ที่อยู่เซิร์ฟเวอร์ปัจจุบัน", "custom_locale": "ปรับภาษาท้องถิ่นเอง", "custom_locale_description": "ใช้รูปแบบวันที่และตัวเลขจากภาษาและขอบเขต", "daily_title_text_date": "E dd MMM", @@ -694,6 +748,7 @@ "disallow_edits": "ไม่อนุญาตให้แก้ไข", "discord": "ดิสคอร์ด", "discover": "ค้นพบ", + "discovered_devices": "ค้นหาอุปกรณ์", "dismiss_all_errors": "ปฏิเสธข้อผิดพลาดทั้งหมด", "dismiss_error": "ปฏิเสธข้อผิดพลาด", "display_options": "ตัวเลือกการแสดง", @@ -704,12 +759,25 @@ "documentation": "เอกสาร", "done": "ดำเนินการสำเร็จ", "download": "ดาวน์โหลด", + "download_canceled": "การดาวน์โหลดยกเลิก", + "download_complete": "การดาวน์โหลดเสร็จสิ้น", + "download_enqueue": "การดาวน์โหลดอยู่ในคิว", + "download_error": "ดาวน์โหลดผิดพลาด", + "download_failed": "ดาวน์โหลดไม่สำเร็จ", + "download_finished": "ดาวน์โหลดเสร็จสิ้น", "download_include_embedded_motion_videos": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหว", "download_include_embedded_motion_videos_description": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหวเมื่อดาวน์โหลดอัลบั้ม", + "download_notfound": "ไม่พบดาวน์โหลด", + "download_paused": "หยุดการดาวน์โหลดชั่วคราว", "download_settings": "การตั้งค่าการดาวน์โหลด", "download_settings_description": "จัดการการตั้งค่าการดาวน์โหลด", + "download_started": "เริ่มการดาวน์โหลด", + "download_sucess": "ดาวน์โหลดสำเร็จ", + "download_sucess_android": "สื่อถูกดาวน์โหลดไปยัง DCIM/Immich", + "download_waiting_to_retry": "รอลองใหม่", "downloading": "กำลังดาวน์โหลด", "downloading_asset_filename": "กำลังดาวน์โหลด {filename}", + "downloading_media": "กำลังดาวน์โหลดสื่อ", "drop_files_to_upload": "วางไฟล์ในช่องอัปโหลด", "duplicates": "รายการที่ซ้ำกัน", "duplicates_description": "แก้ไขแต่ละกลุ่มโดยระบุว่ากลุ่มใดซ้ำกันหากมี", @@ -719,6 +787,8 @@ "edit_avatar": "แก้ไขตัวละคร", "edit_date": "แก้ไขวันที่", "edit_date_and_time": "แก้ไขวันที่และเวลา", + "edit_description": "แก้ไขคำอธิบาย", + "edit_description_prompt": "โปรดเลื่อกคำอธิบายใหม่", "edit_exclusion_pattern": "แก้ไขข้อยกเว้น", "edit_faces": "แก้ไขหน้า", "edit_import_path": "แก้ไขพาธนําเข้า", @@ -739,15 +809,24 @@ "editor_crop_tool_h2_aspect_ratios": "อัตราส่วนภาพ", "editor_crop_tool_h2_rotation": "การหมุน", "email": "อีเมล", + "email_notifications": "แจ้งเตือนผ่านอีเมล", + "empty_folder": "โฟลเดอร์นี้ว่างเปล่า", "empty_trash": "ทิ้งจากถังขยะ", "empty_trash_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการล้างถังขยะ การดำเนินการนี้จะลบทรัพยากรทั้งหมดในถังขยะออกจาก Immich อย่างถาวร\nคุณไม่สามารถย้อนกลับการดำเนินการนี้ได้!", "enable": "เปิดใช้งาน", + "enable_biometric_auth_description": "ใส่พินเพื่อเปิดการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", "enabled": "เปิดใช้งาน", "end_date": "วันสิ้นสุด", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "รอคิว", + "enter_wifi_name": "ใส่ชื่อ Wi-Fi", + "enter_your_pin_code": "ใส่พินโค้ด", + "enter_your_pin_code_subtitle": "ใส่พินโค้ดเพื่อเข้าถึงโฟลเดอร์ล็อค", "error": "เกิดข้อผิดพลาด", + "error_change_sort_album": "เปลี่ยนการเรียงลำดับอัลบั้มไม่สำเร็จ", "error_delete_face": "เกิดเออเรอร์ ไม่สามารถลบใบหน้าออกได้", "error_loading_image": "เกิดข้อผิดพลาดระหว่างโหลดภาพ", + "error_saving_image": "เกิดข้อผิดพลาดระหว่างเซฟภาพ: {error}", + "error_tag_face_bounding_box": "การแท็กใบหน้าผิดพลาด - ไม่สามารถตีกรอบใบหน้าได้", "error_title": "เกิดข้อผิดพลาด", "errors": { "cannot_navigate_next_asset": "ไม่สามารถเปลี่ยนเส้นทางได้", @@ -755,7 +834,7 @@ "cant_apply_changes": "เกิดข้อผิดพลาดในการเปลี่ยนแปลง", "cant_change_activity": "Can't {enabled, select, true {disable} other {enable}} activity", "cant_change_asset_favorite": "ไม่สามารถเปลี่ยนสื่อที่ชื่นชอบได้", - "cant_change_metadata_assets_count": "Can't change metadata of {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "ไม่สามารถแก้ไขข้อมูล metadata ของ {count, plural, one {# สื่อ} other {# สื่อ}}", "cant_get_faces": "เกิดข้อผิดพลาดในการเรียกดูใบหน้า", "cant_get_number_of_comments": "ไม่สามารถเรียกดูจำนวนความคิดเห็นได้", "cant_search_people": "ไม่สามารถค้นหาบุคคลคนได้", @@ -775,10 +854,12 @@ "failed_to_keep_this_delete_others": "ไม่สามารถเก็บหรือลบได้", "failed_to_load_asset": "ไม่สามารถโหลดสื่อได้", "failed_to_load_assets": "ไม่สามารถโหลดสื่อได้", + "failed_to_load_notifications": "โหลดการแจ้งเตือนไม่สำเร็จ", "failed_to_load_people": "ไม่สามารถโหลดบุคคลได้", "failed_to_remove_product_key": "ไม่สามารถลบ product key ได้", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "อัพเดทสถานะการแจ้งเตือนไม่สำเร็จ", "import_path_already_exists": "พาธนำเข้านี้มีอยู่แล้ว", "incorrect_email_or_password": "อีเมลหรือรหัสผ่านไม่ถูกต้อง", "paths_validation_failed": "การตรวจสอบ {paths, plural, one {# path} other {# paths}} ล้มเหลว", @@ -795,6 +876,7 @@ "unable_to_archive_unarchive": "ไม่สามารถทำรายการ {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "ไม่สามารถเปลี่ยนบทบาทผู้ใช้ในอัลบั้มได้", "unable_to_change_date": "ไม่สามารถเปลี่ยนวันที่ได้", + "unable_to_change_description": "ไม่สามารถเปลี่ยนคำอธิบาย", "unable_to_change_favorite": "ไม่สามารถเปลี่ยนแปลงสื่อรายการโปรดได้", "unable_to_change_location": "ไม่สามารถเปลี่ยนตําแหน่งได้", "unable_to_change_password": "ไม่สามารถเปลี่ยนรหัสผ่านได้", @@ -838,6 +920,7 @@ "unable_to_remove_partner": "ไม่สามารถลบคู่หูได้", "unable_to_remove_reaction": "ไม่สามารถลบ reaction ได้", "unable_to_reset_password": "ไม่สามารถตั้งรหัสผ่านใหม่ได้", + "unable_to_reset_pin_code": "ไม่สามารถรีเซ็ตพินโค้ด", "unable_to_resolve_duplicate": "ไม่สามารถแก้ไขของซ้ำได้", "unable_to_restore_assets": "ไม่สามารถเรียกคืนสื่อได้", "unable_to_restore_trash": "ไม่สามารถเรียกคืนถังขยะได้", @@ -865,11 +948,15 @@ "unable_to_update_user": "ไม่สามารถอัพเดทผู้ใช้ได้", "unable_to_upload_file": "ไม่สามารถอัปโหลดได้" }, + "exif": "Exif", "exif_bottom_sheet_description": "เพิ่มคำอธิบาย", "exif_bottom_sheet_details": "รายละเอียด", "exif_bottom_sheet_location": "ตำแหน่ง", "exif_bottom_sheet_people": "คน", "exif_bottom_sheet_person_add_person": "เพิ่มชื่อ", + "exif_bottom_sheet_person_age_months": "อายุ {months} เดือน", + "exif_bottom_sheet_person_age_year_months": "อายุ 1 ปี {months} เดือน", + "exif_bottom_sheet_person_age_years": "อายุ {years} ปี", "exit_slideshow": "ออกจากการนำเสนอ", "expand_all": "ขยายทั้งหมด", "experimental_settings_new_asset_list_subtitle": "กำลังพัฒนา", @@ -886,9 +973,13 @@ "extension": "ส่วนต่อขยาย", "external": "ภายนอก", "external_libraries": "ภายนอกคลังภาพ", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "การเชื่อมต่อภายนอก", + "external_network_sheet_info": "เมื่อไม่ได้เชื่อมต่อ Wi-Fi ที่เลือกไว้ แอพจะเชื่อมต่อเซิร์ฟเวอร์ผ่าน URL ด้านล่างตามลำดับ", "face_unassigned": "ไม่กำหนดมอบหมาย", + "failed": "ล้มเหลว", + "failed_to_authenticate": "การยืนยันตัวตนไม่สำเร็จ", "failed_to_load_assets": "เกิดข้อผิดพลาดในการโหลดสื่อ", + "failed_to_load_folder": "โหลดโฟลเดอร์ไม่สำเร็จ", "favorite": "รายการโปรด", "favorite_or_unfavorite_photo": "โปรดหรือไม่โปรดภาพ", "favorites": "รายการโปรด", @@ -900,18 +991,26 @@ "file_name_or_extension": "นามสกุลหรือชื่อไฟล์", "filename": "ชื่อไฟล์", "filetype": "ชนิดไฟล์", + "filter": "ตัวกรอง", "filter_people": "กรองผู้คน", + "filter_places": "กรองสถานที่", "find_them_fast": "ค้นหาโดยชื่ออย่างรวดเร็ว", "fix_incorrect_match": "แก้ไขการจับคู่ที่ไม่ถูกต้อง", + "folder": "โฟลเดอร์", + "folder_not_found": "ไม่พบโฟลเดอร์", "folders": "โฟล์เดอร์", "folders_feature_description": "การเรียกดูมุมมองโฟลเดอร์สำหรับภาพถ่ายและวิดีโอในระบบไฟล์", "forward": "ไปข้างหน้า", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "ฟีเจอร์นี้ต้องโหลดทรัพยากรจาก Google เพื่อทำงาน", "general": "ทั่วไป", "get_help": "ขอความช่วยเหลือ", + "get_wifiname_error": "ไม่สามารถรับชื่อ Wi-Fi กรุณายืนยันการให้อนุญาตแอพ และยืนยันว่า Wi-Fi เชื่อมต่ออยู่", "getting_started": "เริ่มต้นใช้งาน", "go_back": "กลับ", "go_to_folder": "ไปที่โฟล์เดอร์", "go_to_search": "กลับไปยังการค้นหา", + "grant_permission": "ให้อนุญาต", "group_albums_by": "จัดกลุ่มอัลบั้มตาม", "group_country": "จัดเรียงกลุ่มตามประเทศ", "group_no": "ไม่จัดกลุ่ม", @@ -921,6 +1020,11 @@ "haptic_feedback_switch": "เปิดการตอบสนองแบบสัมผัส", "haptic_feedback_title": "การตอบสนองแบบสัมผัส", "has_quota": "เหลือพื้นที่", + "header_settings_add_header_tip": "เพิ่ม Header", + "header_settings_field_validator_msg": "ค่าต้องไม่ว่างเปล่า", + "header_settings_header_name_input": "ชื่อ Header", + "header_settings_header_value_input": "ค่า Header", + "headers_settings_tile_title": "ปรับแต่ง proxy headers", "hi_user": "สวัสดีคุณ {name} {email}", "hide_all_people": "ซ่อนบุคคลทั้งหมด", "hide_gallery": "ซ่อนคลังภาพ", @@ -929,22 +1033,28 @@ "hide_person": "ซ่อนบุคคล", "hide_unnamed_people": "ซ่อนบุคคลที่ไม่ได้ระบุชื่อ", "home_page_add_to_album_conflicts": "เพิ่ม {added} ทรัพยากรเข้าอัลบั้ม {album}. {failed} ทรัพยากรอยู่ในอัลบั้มอยู่แล้ว", - "home_page_add_to_album_err_local": " ยังไม่สามารถเพิ่มทรัพยากรบนเครื่องเข้าอัลบั้ม กำลังข้าม", + "home_page_add_to_album_err_local": "ยังไม่สามารถเพิ่มสื่อบนอุปกรณ์เข้าอัลบั้ม ข้าม", "home_page_add_to_album_success": "เพิ่มทรัพยากร {added} เข้าอัลบั้ม {album}", - "home_page_album_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_album_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูได้ กำลังข้าม", "home_page_archive_err_local": "ยังไม่สามารถเก็บถาวรได้ กำลังข้าม", - "home_page_archive_err_partner": "ไม่สามารถเก็บทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_archive_err_partner": "ไม่สามารถเก็บสื่อของคู่หูได้ กำลังข้าม", "home_page_building_timeline": "กำลังสร้าง timeline", - "home_page_delete_err_partner": "ไม่สามารถลบทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_delete_err_partner": "ไม่สามารถลบสื่อของคู่ได้ กำลังข้าม", "home_page_delete_remote_err_local": "ทรัพยากรบนเครื่องอยู่ในลบจากรีโมท กำลังข้าม", "home_page_favorite_err_local": "ยังไม่สามารถตั้งทรัพยากรบนเครื่องเป็นรายการโปรด กำลังข้าม", - "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรในรายการโปรดได้ กำลังข้าม", + "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูในรายการโปรดได้ กำลังข้าม", "home_page_first_time_notice": "ถ้าครั้งนี้เป็นครั้งแรกที่ใช้แอปนี้ กรุณาเลือกอัลบั้มที่จะสำรองข้อมูล ไทม์ไลน์จะได้เพิ่มรูปภาพและวิดีโอที่อยู่ในอัลบั้ม", + "home_page_locked_error_local": "ไม่สามารถย้ายสื่อบนอุปกรณ์ไปยังโฟลเดอร์ล็อค ข้าม", + "home_page_locked_error_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูไปยังโฟลเดอร์ล็อคได้ กำลังข้าม", "home_page_share_err_local": "ไม่สามารถแชร์ผ่านลิงค์ได้ กำลังข้าม", "home_page_upload_err_limit": "สามารถอัพโหลดได้มากสุดครั้งละ 30 ทรัพยากร กำลังข้าม", "host": "โฮสต์", "hour": "ชั่วโมง", + "id": "ไอดี", + "ignore_icloud_photos": "ข้ามภาพบน iCloud", + "ignore_icloud_photos_description": "ภาพที่ถูกเก็บบน iCloud จะไม่ถูกอัพโหลดขึ้น Immich", "image": "รูปภาพ", + "image_alt_text_date": "{isVideo, select, true {วิดีโอ} other {รูปภาพ}}ถูกถ่ายเมื่อ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} วันที่ {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1}, {person2},และ {person3} วันที่ {date}", @@ -954,6 +1064,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2},และ {person3} วันที่ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2}, และ {additionalCount, number} ในวันที่ {date}", + "image_saved_successfully": "รูปภาพถูกเซฟ", "image_viewer_page_state_provider_download_started": "ดาวน์โหลดเริ่มต้น", "image_viewer_page_state_provider_download_success": "ดาวน์โหลดสำเร็จ", "image_viewer_page_state_provider_share_error": "แชร์ผิดพลาด", @@ -974,8 +1085,16 @@ "night_at_midnight": "ทุกเที่ยงคืน", "night_at_twoam": "ทุกวันเวลาตี 2" }, + "invalid_date": "วันที่ไม่ถูกต้อง", + "invalid_date_format": "รูปแบบวันที่ไม่ถูกต้อง", "invite_people": "เชิญผู้คน", "invite_to_album": "เชิญเข้าอัลบั้ม", + "ios_debug_info_fetch_ran_at": "รับข้อมูลเมื่อ {dateTime}", + "ios_debug_info_last_sync_at": "ซิงค์ล่าสุด {dateTime}", + "ios_debug_info_no_processes_queued": "ไม่มีคิวในพื้นหลัง", + "ios_debug_info_no_sync_yet": "ยังไม่มีงานซิงค์รันในพื้นหลัง", + "ios_debug_info_processes_queued": "{count} โพรเซสรอคิวในพื้นหลัง", + "ios_debug_info_processing_ran_at": "โพรเซสรันเมื่อ {dateTime}", "items_count": "{count, plural, one {# รายการ} other {#รายการ}}", "jobs": "งาน", "keep": "เก็บ", @@ -984,6 +1103,9 @@ "kept_this_deleted_others": "เก็บเนื้อหานี้และลบ {count, plural, one {# Asset} other {# Asset}}", "keyboard_shortcuts": "ปุ่มพิมพ์ลัด", "language": "ภาษา", + "language_no_results_subtitle": "กรุณาปรับเปลี่ยนคำค้นหา", + "language_no_results_title": "ไม่พบภาษา", + "language_search_hint": "ค้นหาภาษา...", "language_setting_description": "เลือกภาษาที่ต้องการ", "last_seen": "เห็นล่าสุด", "latest_version": "เวอร์ชันล่าสุด", @@ -1009,14 +1131,21 @@ "list": "รายการ", "loading": "กำลังโหลด", "loading_search_results_failed": "โหลดผลการค้นหาล้มเหลว", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "ไม่สามารถแคสสื่อที่ไม่ถูกอัพโหลดไปยังเซิร์ฟเวอร์", + "local_network": "เครือข่ายระยะใกล้", + "local_network_sheet_info": "แอพจะทำการเชื่อมต่อไปยังเซิร์ฟเวอร์ผ่าน URL นี้เมื่อเชื่อต่อกับ Wi-Fi ที่เลือกไว้", + "location_permission": "การอนุญาตตำแหน่ง", + "location_permission_content": "เพื่อใช้ฟีเจอร์การสับโดยอัตโนมัติ Immich ต้องการการอนุญาตเข้าถึงต่ำแหน่งที่แม่นยำเพื่ออ่านชื่อ Wi-Fi ที่เชื่อมต่ออยู่", "location_picker_choose_on_map": "เลือกบนแผนที่", "location_picker_latitude_error": "กรุณาเพิ่มละติจูตที่ถูกต้อง", "location_picker_latitude_hint": "เพิ่มละติจูตตรงนี้", "location_picker_longitude_error": "กรุณาเพิ่มลองจิจูตที่ถูกต้อง", "location_picker_longitude_hint": "เพิ่มลองจิจูตตรงนี้", + "lock": "ล็อค", + "locked_folder": "โฟลเดอร์ล็อค", "log_out": "ออกจากระบบ", "log_out_all_devices": "ให้ทุกอุปกรณ์ออกจากระบบทั้งหมด", + "logged_in_as": "{user} กำลังล็อคอิน", "logged_out_all_devices": "ออกจากระบบทั้งหมดแล้ว", "logged_out_device": "ออกจากระบบแล้ว", "login": "เข้าสู่ระบบ", @@ -1079,7 +1208,7 @@ "map_settings_date_range_option_years": "{years} ปีผ่านมา", "map_settings_dialog_title": "ตั้งค่าแผนที่", "map_settings_include_show_archived": "รวมเก็บถาวร", - "map_settings_include_show_partners": "รามพันธมิตร", + "map_settings_include_show_partners": "รวมคู่หู", "map_settings_only_show_favorites": "แสดงรายการโปรดเท่านั้น", "map_settings_theme_settings": "ธีมแผนที่", "map_zoom_to_see_photos": "ซูมออกเพื่อดูรูป", @@ -1106,12 +1235,17 @@ "model": "โมเดล", "month": "เดือน", "more": "เพิ่มเติม", + "move": "ย้าย", + "move_off_locked_folder": "ย้ายออกจากโฟลเดอร์ล็อค", + "move_to_locked_folder": "ย้ายไปโฟลเดอร์ล็อค", "moved_to_trash": "ทิ้งลงถังขยะแล้ว", "multiselect_grid_edit_date_time_err_read_only": "ไม่สามารถแก้ไขวันที่ทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "multiselect_grid_edit_gps_err_read_only": "ไม่สามารถแก้ตำแหน่งของทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "my_albums": "อัลบั้มของฉัน", "name": "ชื่อ", "name_or_nickname": "ชื่อหรือชื่อเล่น", + "networking_settings": "การเชื่อมต่อ", + "networking_subtitle": "ตั้งค่าปลายทางเซิร์ฟเวอร์", "never": "ไม่เคย", "new_album": "อัลบั้มใหม่", "new_api_key": "สร้าง API คีย์ใหม่", @@ -1155,7 +1289,7 @@ "ok": "ตกลง", "oldest_first": "เรียงเก่าสุดก่อน", "onboarding": "การเริ่มต้นใช้งาน", - "onboarding_privacy_description": "คุณลักษณะ (ไม่จำเป็น) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการดูแลระบบ", + "onboarding_privacy_description": "ฟีเจอร์ (ตัวเลือก) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการ", "onboarding_theme_description": "เลือกธีมสี คุณสามารถเปลี่ยนแปลงได้ในภายหลังในการตั้งค่าของคุณ", "onboarding_welcome_user": "ยินดีต้อนรับคุณ {user}", "online": "ออนไลน์", @@ -1172,20 +1306,20 @@ "other_variables": "ตัวแปรอื่น", "owned": "เป็นเจ้าของ", "owner": "เจ้าของ", - "partner": "พาร์ทเนอร์", + "partner": "คู่หู", "partner_can_access": "{partner} สามารถเข้าถึง", "partner_can_access_assets": "รูปภาพและวิดีโอทั้งหมดยกเว้นที่อยู่ในเก็บถาวรและถูกลบทิ้ง", "partner_can_access_location": "ตำแหน่งที่รูปถูกถ่าย", "partner_list_user_photos": "รูปภาพของ {user}", "partner_list_view_all": "ดูทั้งหมด", - "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับพันธมิตร", + "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับคู่หู", "partner_page_no_more_users": "ไม่มีผู้ใช้งานให้เพิ่ม", - "partner_page_partner_add_failed": "การเพิ่มพันธมิตรล้มเหลว", - "partner_page_select_partner": "เลือกพันธมิตร", + "partner_page_partner_add_failed": "การเพิ่มคู่หูล้มเหลว", + "partner_page_select_partner": "เลือกคู่หู", "partner_page_shared_to_title": "แชร์กับ", "partner_page_stop_sharing_content": "{partner} จะไม่สามารถเข้าถึงรูปภาพของคุณ", - "partner_sharing": "แชร์สำหรับพาร์ทเนอร์", - "partners": "พาร์ทเนอร์", + "partner_sharing": "แชร์สำหรับคู่หู", + "partners": "คู่หู", "password": "รหัสผ่าน", "password_does_not_match": "รหัสผ่านไม่ตรงกัน", "password_required": "จำเป็นต้องมีรหัสผ่าน", @@ -1276,7 +1410,7 @@ "purchase_lifetime_description": "ซื้อตลอดชีพ", "purchase_option_title": "ตัวเลือกการซื้อ", "purchase_panel_info_1": "ทางทีม Immich ต้องใช้เวลาและความพยายามอย่างมากในการพัฒนาระบบนี้ขึ้นมา และเรามีวิศวกรที่ทำงานเต็มเวลาเพื่อพัฒนาให้ดีที่สุดเท่าที่จะทำได้ ภารกิจของเราคือการทำให้ซอฟต์แวร์โอเพ่นซอร์สและแนวทางปฏิบัติทางธุรกิจที่ถูกต้องตามจริยธรรมกลายเป็นแหล่งรายได้ที่ยั่งยืนสำหรับนักพัฒนา และสร้างระบบนิเวศที่เคารพความเป็นส่วนตัวพร้อมทางเลือกอื่นที่เป็นรูปธรรมแทนบริการคลาวด์ที่เอารัดเอาเปรียบ", - "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบท่านในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", + "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบคุณในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", "purchase_panel_title": "สนับสนุนโครงการนี้", "purchase_per_server": "ต่อเซิร์ฟเวอร์", "purchase_per_user": "ต่อผู้ใช้งาน", @@ -1421,6 +1555,7 @@ "select_keep_all": "เลือกเก็บทั้งหมด", "select_library_owner": "เลือกเจ้าของคลังภาพ", "select_new_face": "เลือกใบหน้าใหม่", + "select_person_to_tag": "เลือกบุคคล", "select_photos": "เลือกรูปภาพ", "select_trash_all": "เลือกในถังขยะทั้งหมด", "select_user_for_sharing_page_err_album": "สร้างอัลบั้มล้มเหลว", @@ -1428,12 +1563,14 @@ "selected_count": "{count, plural, other {# เลือกแล้ว}}", "send_message": "ส่งข้อความ", "send_welcome_email": "ส่งอีเมลต้อนรับ", + "server_endpoint": "ปลายทางเซิร์ฟเวอร์", "server_info_box_app_version": "เวอร์ชันแอพ", "server_info_box_server_url": "URL เซิร์ฟเวอร์", "server_offline": "Server ออฟไลน์", "server_online": "Server ออนไลน์", + "server_privacy": "ความเป็นส่วนตัวเซิร์ฟเวอร์", "server_stats": "สถิติเซิร์ฟเวอร์", - "server_version": "เวอร์ชันของ Server", + "server_version": "เวอร์ชันของเซิร์ฟเวอร์", "set": "ตั้ง", "set_as_album_cover": "ตั้งเป็นภาพปกอัลบั้ม", "set_as_featured_photo": "ตั้งเป็นรูปสำคัญ", @@ -1518,7 +1655,7 @@ "sharing_page_empty_list": "รายการว่างเปล่า", "sharing_sidebar_description": "แสดงลิงก์ที่แชร์ในแถบด้านข้าง", "sharing_silver_appbar_create_shared_album": "อัลบั้มที่แชร์ใหม่", - "sharing_silver_appbar_share_partner": "แชร์กับพันธมิตร", + "sharing_silver_appbar_share_partner": "แชร์กับคู่หู", "shift_to_permanent_delete": "กด ⇧ to สำหรับลบสื่อถาวร", "show_album_options": "แสดงตัวเลือกอัลบั้ม", "show_albums": "แสดงอัลบั้ม", @@ -1570,7 +1707,7 @@ "status": "สถานะ", "stop_motion_photo": "ภาพวัตถุเคลื่อนไหว", "stop_photo_sharing": "หยุดแชร์รูปภาพ?", - "stop_photo_sharing_description": "{partner}จะไม่สามารถเข้าถึงรูปของคุณได้อีก", + "stop_photo_sharing_description": "{partner} จะไม่สามารถเข้าถึงรูปของคุณได้อีก", "stop_sharing_photos_with_user": "หยุดการแชร์รูปภาพของคุณกับผู้ใช้นี้", "storage": "พื้นที่จัดเก็บ", "storage_label": "เนื้อที่จัดเก็บ", @@ -1644,6 +1781,7 @@ "unselect_all": "ยกเลิกการเลือกทั้งหมด", "unstack": "หยุดซ้อน", "up_next": "ต่อไป", + "updated_at": "อัพเดท", "updated_password": "รหัสผ่านเปลี่ยนแล้ว", "upload": "อัปโหลด", "upload_concurrency": "อัปโหลดพร้อมกัน", @@ -1653,7 +1791,9 @@ "upload_status_errors": "ข้อผิดพลาด", "upload_status_uploaded": "อัปโหลดแล้ว", "upload_success": "อัปโหลดสำเร็จ, รีเฟรชหน้านี้ใหม่คุณจะเห็นสื่อที่เพิ่มล่าสุด", + "uploading": "กำลังอัพโหลด", "usage": "การใช้งาน", + "use_biometric": "ใช้การพิสูจน์อัตลักษณ์", "use_custom_date_range": "ใช้การปรับแต่งช่วงเวลา", "user": "ผู้ใช้", "user_id": "ไอดีผู้ใช้", @@ -1686,6 +1826,7 @@ "view_links": "ดูลิงก์", "view_next_asset": "ดูสื่อถัดไป", "view_previous_asset": "ดูสื่อก่อนหน้า", + "view_qr_code": "ดูคิวอาร์โค้ด", "viewer_remove_from_stack": "เอาออกจากที่ซ้อน", "viewer_stack_use_as_main_asset": "ใช้เป็นทรัพยากรหลัก", "viewer_unstack": "หยุดซ้อน", @@ -1695,11 +1836,11 @@ "week": "สัปดาห์", "welcome": "ยินดีต้อนรับ", "welcome_to_immich": "ยินดีต้อนรับสู่ immich", - "wifi_name": "WiFi Name", + "wifi_name": "ชื่อ Wi-Fi", "year": "ปี", "years_ago": "{years, plural, one {# ปี} other {# ปี}} ที่แล้ว", "yes": "ใช่", "you_dont_have_any_shared_links": "คุณไม่ได้มีลิงก์ที่แชร์", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "ชื่อ Wi-Fi", "zoom_image": "ซูมรูปภาพ" } diff --git a/i18n/tr.json b/i18n/tr.json index ddb99ca019..b26f70acc2 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} fotoğraf favorilere eklendi", "admin": { "add_exclusion_pattern_description": "Hariç tutma desenleri ekleyin. *, ** ve ? kullanılarak Globbing (temsili yer doldurucu karakter) desteklenir. Farzedelim \"Raw\" adlı bir dizininiz var, içinde ki tüm dosyaları yoksaymak için \"**/Raw/**\" şeklinde yazabilirsiniz. \".tif\" ile biten tüm dosyaları yoksaymak için \"**/*.tif\" yazabilirsiniz. Mutlak yolu yoksaymak için \"/yoksayılacak/olan/yol/**\" şeklinde yazabilirsiniz.", + "admin_user": "Yönetici kullanıcısı", "asset_offline_description": "Bu harici kütüphane varlığı artık diskte bulunmuyor ve çöp kutusuna taşındı. Dosya kütüphane içinde taşındıysa, yeni karşılık gelen varlık için zaman çizelgenizi kontrol edin. Bu varlığı geri yüklemek için lütfen aşağıdaki dosya yolunun Immich tarafından erişilebilir olduğundan emin olun ve kütüphaneyi tarayın.", "authentication_settings": "Yetkilendirme Ayarları", "authentication_settings_description": "Şifre, OAuth, ve diğer yetkilendirme ayarlarını yönet", @@ -44,7 +45,7 @@ "backup_database_enable_description": "Veritabanı yığınlarını etkinleştir", "backup_keep_last_amount": "Tutulması gereken geçmiş yığını miktarı", "backup_settings": "Veritabanı yığını ayarları", - "backup_settings_description": "Veritabanı Yedekleme Ayarlarını Yönet", + "backup_settings_description": "Veritabanı döküm ayarlarını yönet.", "cleared_jobs": "{job} için işler temizlendi", "config_set_by_file": "Ayarlar şuanda config dosyası tarafından ayarlanmıştır", "confirm_delete_library": "{library} kütüphanesini silmek istediğinize emin misiniz?", @@ -68,7 +69,9 @@ "force_delete_user_warning": "UYARI: Bu işlem kullanıcıyı ve tüm varlıkları anında kaldıracaktır. Bu geri alınamaz ve dosyalar geri getirilemez.", "image_format": "Biçim", "image_format_description": "WebP, JPEG'e göre daha küçük dosya boyutu sunar fakat işlemesi daha uzun sürer.", + "image_fullsize_description": "Yakınlaştırıldığında kullanılan, meta verileri kaldırılmış tam boyutlu görüntü", "image_fullsize_enabled": "Tam boyutlu görüntü üretimini etkinleştir", + "image_fullsize_enabled_description": "Yerleşik önizlemeyi tercih et” seçeneği etkinleştirildiğinde, yerleşik önizlemeler dönüştürme yapılmadan doğrudan kullanılır. JPEG gibi web dostu formatlar bu ayardan etkilenmez.", "image_fullsize_quality_description": "1-100 arasında tam boyutlu görüntü kalitesi. Daha yüksek kalitelidir, ancak daha büyük dosyalar üretir.", "image_fullsize_title": "Tam boyutlu görüntü ayarları", "image_prefer_embedded_preview": "Gömülü önizlemeyi tercih et", @@ -168,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", "note_cannot_be_changed_later": "NOT: Bu daha sonra değiştirilemez!", "notification_email_from_address": "Şu adresten", - "notification_email_from_address_description": "Göndericinin email adresi, örnek: \"Immich Fotoğraf Sunucusu \"", + "notification_email_from_address_description": "Gönderen e-posta adresi, örneğin: \"Immich Görsel Sunucusu \". E-posta gönderilmesine izin verdiğiniz bir adres kullandığınızdan emin olun.", "notification_email_host_description": "E-posta sunucusunun ana bilgisayarı (örneğin, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Sertifika hatalarını görmezden gel", "notification_email_ignore_certificate_errors_description": "TLS sertifika doğrulama ayarlarını görmezden gel (Önerilmez)", @@ -188,6 +191,7 @@ "oauth_auto_register": "Otomatik kayıt", "oauth_auto_register_description": "OAuth ile giriş yapan yeni kullanıcıları otomatik kaydet", "oauth_button_text": "Buton yazısı", + "oauth_client_secret_description": "OAuth sağlayıcısı PKCE (Kod Değişimi İçin Kanıt Anahtarı) desteği sunmuyorsa gereklidir", "oauth_enable_description": "OAuth ile giriş yap", "oauth_mobile_redirect_uri": "Mobil yönlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanılacak Yönlendirme Adresi", @@ -200,7 +204,7 @@ "oauth_storage_quota_claim": "Depolama kotası talebi", "oauth_storage_quota_claim_description": "Kullanıcıya depolama kotası koymak için kullanılacak değer (en: OAuth claim).", "oauth_storage_quota_default": "Varsayılan depolama kotası (GiB)", - "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse konulacak kota. GiB cinsinden, sınırsız kota için 0 kullanın.", + "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse GiB cinsinden konulacak kota.", "oauth_timeout": "İstek zaman aşımı", "oauth_timeout_description": "Milisaniye cinsinden istek zaman aşımı", "password_enable_description": "Email ve şifre ile giriş yap", @@ -237,9 +241,10 @@ "storage_template_hash_verification_enabled_description": "Hash doğrulamayı etkinleştirir, eğer ne işe yaradığını bilmiyorsanız bunu devre dışı bırakmayın", "storage_template_migration": "Depolama şablonu birleştirme", "storage_template_migration_description": "Geçerli {template} ayarlarını daha önce yüklenmiş olan varlıklara uygula", - "storage_template_migration_info": "Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", + "storage_template_migration_info": "Depolama şablonu tüm dosya uzantılarını küçük harfe dönüştürecektir. Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", "storage_template_migration_job": "Depolama Adreslerini Değiştirme Görevi", "storage_template_more_details": "Bu özellik hakkında daha fazla bilgi için, Depolama Şablonu ve onun etkileri kısmına bakın", + "storage_template_onboarding_description_v2": "Etkinleştirildiğinde, bu özellik dosyaları kullanıcı tanımlı bir şablona göre otomatik olarak organize eder. Daha fazla bilgi için lütfen belgelere bakın.", "storage_template_path_length": "Tahmini dosya adresi uzunluğu: {length, number}/{limit, number}", "storage_template_settings": "Depolama Şablonu", "storage_template_settings_description": "Yüklenen dosyanın ismini ve klasör yapısını düzenle", @@ -254,7 +259,7 @@ "template_email_update_album": "Albüm Şablonunu Güncelle", "template_email_welcome": "Hoş geldiniz e-posta şablonu", "template_settings": "Bildirim Şablonları", - "template_settings_description": "Bildirim şablonlarını yönet.", + "template_settings_description": "Bildirim şablonlarını yönet", "theme_custom_css_settings": "Özel CSS", "theme_custom_css_settings_description": "CSS (Cascading Style Sheets) kullanılarak Immich'in tasarımı değiştirilebilir.", "theme_settings": "Tema ayarları", @@ -286,13 +291,13 @@ "transcoding_encoding_options": "Kodlama Seçenekleri", "transcoding_encoding_options_description": "Kodlanmış videolar için kodekleri, çözünürlüğü, kaliteyi ve diğer seçenekleri ayarlayın", "transcoding_hardware_acceleration": "Donanım Hızlandırma", - "transcoding_hardware_acceleration_description": "Deneysel; daha hızlı, fakat aynı bitrate ayarlarında daha düşük kaliteye sahip", + "transcoding_hardware_acceleration_description": "Deneysel: daha hızlı dönüştürme, ancak aynı bit hızında kaliteyi düşürebilir", "transcoding_hardware_decoding": "Donanım çözücü", "transcoding_hardware_decoding_setting_description": "Uçtan uca hızlandırmayı, sadece kodlamayı hızlandırmanın yerine etkinleştirir. Tüm videolarda çalışmayabilir.", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yüksek değerler sıkıştırma verimliliğini artırır, ancak kodlamayı yavaşlatır. Eski cihazlarda donanım hızlandırma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dışı bırakır, -1 ise bu değeri otomatik olarak ayarlar.", "transcoding_max_bitrate": "Maksimum bitrate", - "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteye küçük bir maliyetle dosya boyutlarını daha öngörülebilir hale getirebilir.", + "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteyi az bir maliyetle düşürerek dosya boyutlarını daha öngörülebilir hale getirebilir. 720p çözünürlükte, tipik değerler VP9 veya HEVC için 2600 kbit/s, H.264 için ise 4500 kbit/s’dir. 0 olarak ayarlanırsa devre dışı bırakılır.", "transcoding_max_keyframe_interval": "Maksimum ana kare aralığı", "transcoding_max_keyframe_interval_description": "Ana kareler arasındaki maksimum kare mesafesini ayarlar. Düşük değerler sıkıştırma verimliliğini kötüleştirir, ancak arama sürelerini iyileştirir ve hızlı hareket içeren sahnelerde kaliteyi artırabilir. 0 bu değeri otomatik olarak ayarlar.", "transcoding_optimal_description": "Hedef çözünürlükten yüksek veya kabul edilen formatta olmayan videolar", @@ -352,6 +357,7 @@ "admin_password": "Yönetici Şifresi", "administration": "Yönetim", "advanced": "Gelişmiş", + "advanced_settings_enable_alternate_media_filter_subtitle": "Eşleme sırasında medyayı alternatif ölçütlere göre süzgeçten geçirmek için bu seçeneği kullanın. Uygulamanın tüm albümleri algılamasında sorun yaşıyorsanız yalnızca bu durumda deneyin.", "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albüm eşleme süzgeci kullanın", "advanced_settings_log_level_title": "Günlük düzeyi: {level}", "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar, cihazdaki öğelerin küçük resimlerini göstermekte çok yavaştır. Bunun yerine sunucudaki küçük resimleri göstermek için bu ayarı etkinleştirin.", @@ -360,6 +366,7 @@ "advanced_settings_proxy_headers_title": "Proxy Header'lar", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktası için SSL sertifika doğrulamasını atlar. Kendinden imzalı sertifikalar için gereklidir.", "advanced_settings_self_signed_ssl_title": "Kendi kendine imzalanmış SSL sertifikalarına izin ver", + "advanced_settings_sync_remote_deletions_subtitle": "Web üzerinde işlem yapıldığında, bu aygıttaki varlığı otomatik olarak sil veya geri yükle", "advanced_settings_sync_remote_deletions_title": "Uzaktan silinmeleri eşle [DENEYSEL]", "advanced_settings_tile_subtitle": "Gelişmiş kullanıcı ayarları", "advanced_settings_troubleshooting_subtitle": "Sorun giderme için ek özellikleri etkinleştirin", @@ -392,11 +399,14 @@ "album_viewer_appbar_share_err_remove": "Albümden öğeleri kaldırmada sorunlar var", "album_viewer_appbar_share_err_title": "Albüm başlığı değiştirilemedi", "album_viewer_appbar_share_leave": "Albümden çık", - "album_viewer_appbar_share_to": "Paylaş:", + "album_viewer_appbar_share_to": "Paylaşma", "album_viewer_page_share_add_users": "Kullanıcı ekle", "album_with_link_access": "Link'e sahip olan herhangi bir kişinin bu albümdeki fotoğrafları ve kişileri görmesine izin ver.", "albums": "Albümler", "albums_count": "{count, plural, one {{count, number} Albüm} other {{count, number} Albüm}}", + "albums_default_sort_order": "Varsayılan albüm sıralama düzeni", + "albums_default_sort_order_description": "Yeni albüm oluştururken kullanılacak başlangıç varlık sıralama düzeni.", + "albums_feature_description": "Diğer kullanıcılarla paylaşılabilen varlık koleksiyonları.", "all": "Tümü", "all_albums": "Tüm Albümler", "all_people": "Tüm Kişiler", @@ -417,6 +427,7 @@ "app_settings": "Uygulama Ayarları", "appears_in": "Şurada görünür", "archive": "Arşiv", + "archive_action_prompt": "{count} arşive eklendi", "archive_or_unarchive_photo": "Fotoğrafı arşivle/arşivden çıkar", "archive_page_no_archived_assets": "Arşivlenmiş öğe bulunamadı", "archive_page_title": "Arşiv ({count})", @@ -454,10 +465,12 @@ "assets": "Varlıklar", "assets_added_count": "{count, plural, one {# varlık eklendi} other {# varlık eklendi}}", "assets_added_to_album_count": "{count, plural, one {# varlık} other {# varlık}} albüme eklendi", - "assets_added_to_name_count": "{count, plural, one {# varlık} other {# varlık}} {hasName, select, true {{name}} other {yeni albüm}} içine eklendi", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Varlık} other {Varlıklar}} albüme eklenemiyor", "assets_count": "{count, plural, one {# varlık} other {# varlıklar}}", "assets_deleted_permanently": "{count} öğe kalıcı olarak silindi", "assets_deleted_permanently_from_server": "{count} öğe kalıcı olarak Immich sunucusundan silindi", + "assets_downloaded_failed": "{count, plural, one {# dosya indirildi – {error} dosya indirilemedi} other {# dosya indirildi – {error} dosya indirilemedi}}", + "assets_downloaded_successfully": "{count, plural, one {# dosya başarıyla indirildi} other {# dosya başarıyla indirildi}}", "assets_moved_to_trash_count": "{count, plural, one {# varlık} other {# varlık}} çöpe taşındı", "assets_permanently_deleted_count": "Kalıcı olarak silindi {count, plural, one {# varlık} other {# varlıklar}}", "assets_removed_count": "Kaldırıldı {count, plural, one {# varlık} other {# varlıklar}}", @@ -484,12 +497,12 @@ "backup_album_selection_page_selection_info": "Seçim Bilgileri", "backup_album_selection_page_total_assets": "Toplam eşsiz öğeler", "backup_all": "Tümü", - "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor...", - "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor...", + "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor…", + "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor…", "backup_background_service_current_upload_notification": "{filename} yükleniyor", "backup_background_service_default_notification": "Yeni öğeler kontrol ediliyor…", "backup_background_service_error_title": "Yedekleme hatası", - "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor...", + "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor…", "backup_background_service_upload_failure_notification": "{filename} yüklemesi başarısız oldu", "backup_controller_page_albums": "Yedekleme Albümleri", "backup_controller_page_background_app_refresh_disabled_content": "Arka planda yedeklemeyi kullanabilmek için Ayarlar > Genel > Arka Planda Uygulama Yenileme bölümünden arka planda uygulama yenilemeyi etkinleştirin.", @@ -577,6 +590,8 @@ "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alınamaz!", "cannot_update_the_description": "Açıklama güncellenemiyor", + "cast": "Yansıt", + "cast_description": "Kullanılabilir yansıtma hedeflerini yapılandır", "change_date": "Tarihi değiştir", "change_description": "Açıklamayı değiştir", "change_display_order": "Görüntüleme sırasını değiştir", @@ -636,6 +651,7 @@ "confirm_tag_face": "Bu yüzü {name} olarak etiketlemek ister misiniz?", "confirm_tag_face_unnamed": "Bu yüzü etiketlemek ister misin?", "connected_device": "Cihaz bağlandı", + "connected_to": "Bağlı", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", @@ -644,8 +660,8 @@ "control_bottom_app_bar_delete_from_local": "Cihazdan sil", "control_bottom_app_bar_edit_location": "Konumu Düzenle", "control_bottom_app_bar_edit_time": "Tarih ve Saati Düzenle", - "control_bottom_app_bar_share_link": "İlişimi paylaş", - "control_bottom_app_bar_share_to": "Paylaş:", + "control_bottom_app_bar_share_link": "Bağlantıyı Paylaş", + "control_bottom_app_bar_share_to": "Paylaşma", "control_bottom_app_bar_trash_from_immich": "Çöp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandı.", "copied_to_clipboard": "Panoya kopyalandı!", @@ -687,6 +703,7 @@ "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", + "dark_theme": "Karanlık temaya geç", "date_after": "Sonraki tarih", "date_and_time": "Tarih ve Zaman", "date_before": "Önceki tarih", @@ -702,6 +719,7 @@ "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", + "delete_action_prompt": "{count} kalıcı olarak silindi", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu öğeler cihazınızdan ve Immich'ten kalıcı olarak silinecektir", @@ -715,6 +733,7 @@ "delete_key": "Anahtarı sil", "delete_library": "Kütüphaneyi sil", "delete_link": "Bağlantıyı sil", + "delete_local_action_prompt": "{count} yerel olarak silindi", "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", @@ -734,6 +753,7 @@ "disallow_edits": "Değişikliklere izin verme", "discord": "Discord", "discover": "Keşfet", + "discovered_devices": "Keşfedilen aygıtlar", "dismiss_all_errors": "Tüm hataları yoksay", "dismiss_error": "Hatayı yoksay", "display_options": "Görüntüleme seçenekleri", @@ -802,6 +822,7 @@ "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasını etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", + "enqueued": "Kuyruğa alındı", "enter_wifi_name": "Wi-Fi adını girin", "enter_your_pin_code": "Pin kodu girin", "enter_your_pin_code_subtitle": "Kilitli klasöre erişmek için PIN kodunuzu girin", @@ -810,6 +831,7 @@ "error_delete_face": "Yüzü varlıktan silme hatası", "error_loading_image": "Resim yüklenirken hata oluştu", "error_saving_image": "Hata: {error}", + "error_tag_face_bounding_box": "Yüz etiketleme hatası – sınırlayıcı kutu koordinatları alınamadı", "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapılamıyor", @@ -859,6 +881,7 @@ "unable_to_archive_unarchive": "{archived, select, true {Arşivleme} other {Arşivden çıkarma}} işlemi yapılamıyor", "unable_to_change_album_user_role": "Albüm kullanıcı rolü değiştirilemiyor", "unable_to_change_date": "Tarih değiştirilemiyor", + "unable_to_change_description": "Açıklama değiştirilemiyor", "unable_to_change_favorite": "Favori durumu değiştirilemiyor", "unable_to_change_location": "Konum değiştirilemiyor", "unable_to_change_password": "Şifre değiştirilemiyor", @@ -902,6 +925,7 @@ "unable_to_remove_partner": "Ortak kaldırılamıyor", "unable_to_remove_reaction": "Reaksiyon kaldırılamıyor", "unable_to_reset_password": "Şifre sıfırlanamıyor", + "unable_to_reset_pin_code": "Pin kodunu sıfırlanamıyor", "unable_to_resolve_duplicate": "Çiftler çözümlenemiyor", "unable_to_restore_assets": "Varlıklar geri yüklenemiyor", "unable_to_restore_trash": "Çöp geri yüklenemiyor", @@ -935,6 +959,9 @@ "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", "exif_bottom_sheet_person_add_person": "İsim ekle", + "exif_bottom_sheet_person_age_months": "Yaş: {months} ay", + "exif_bottom_sheet_person_age_year_months": "Yaş: 1 yıl, {months} ay", + "exif_bottom_sheet_person_age_years": "Yaş: {years}", "exit_slideshow": "Slayt gösterisinden çık", "expand_all": "Hepsini genişlet", "experimental_settings_new_asset_list_subtitle": "Çalışmalar devam ediyor", @@ -952,13 +979,17 @@ "external": "Harici", "external_libraries": "Harici kütüphaneler", "external_network": "Harici ağlar", - "external_network_sheet_info": "Belirlenmiş WiFi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", + "external_network_sheet_info": "Belirlenmiş Wi-Fi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", "face_unassigned": "Yüz atanmadı", + "failed": "Başarısız", + "failed_to_authenticate": "Kimlik doğrulaması yapılamadı", "failed_to_load_assets": "Varlıklar yüklenemedi", - "favorite": "Favori", - "favorite_or_unfavorite_photo": "Favoriye ekle veya çıkar", - "favorites": "Favoriler", - "favorites_page_no_favorites": "Favori öğe bulunamadı", + "failed_to_load_folder": "Klasör yüklenemedi", + "favorite": "Gözde", + "favorite_action_prompt": "{count} gözdelere eklendi", + "favorite_or_unfavorite_photo": "Gözdeye ekle veya çıkar", + "favorites": "Gözdeler", + "favorites_page_no_favorites": "Gözde öge bulunamadı", "feature_photo_updated": "Özellikli fotoğraf güncellendi", "features": "Özellikler", "features_setting_description": "Uygulamanın özelliklerini yönet", @@ -968,11 +999,16 @@ "filetype": "Dosya tipi", "filter": "Filtre", "filter_people": "Kişileri filtrele", + "filter_places": "Yerleri süz", "find_them_fast": "Adlarına göre hızlıca bul", "fix_incorrect_match": "Yanlış eşleştirmeyi düzelt", + "folder": "Klasör", + "folder_not_found": "Klasör bulunamadı", "folders": "Klasörler", "folders_feature_description": "Dosya sistemindeki fotoğraf ve videoları klasör görünümüyle keşfedin", "forward": "İleri", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Bu özellik, çalışabilmek için Google'dan harici kaynaklar yükler.", "general": "Genel", "get_help": "Yardım Al", "get_wifiname_error": "Wi-Fi adı alınamadı. Gerekli izinleri verdiğinizden ve bir Wi-Fi ağına bağlı olduğunuzdan emin olun", @@ -1012,12 +1048,16 @@ "home_page_building_timeline": "Zaman çizelgesi oluşturuluyor", "home_page_delete_err_partner": "Partner öğeleri silinemez, atlanıyor", "home_page_delete_remote_err_local": "Uzaktan silme seçimindeki yerel öğeler atlanıyor", - "home_page_favorite_err_local": "Yerel öğeler henüz favorilere eklenemiyor, atlanıyor", - "home_page_favorite_err_partner": "Partner öğeleri henüz favorilere eklenemiyor, atlanıyor", + "home_page_favorite_err_local": "Yerel ögeler henüz gözdelere eklenemiyor, atlanıyor", + "home_page_favorite_err_partner": "Ortak ögeleri henüz gözdelere eklenemiyor, atlanıyor", "home_page_first_time_notice": "Uygulamayı ilk kez kullanıyorsanız, zaman çizelgesinin albümlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lütfen yedekleme için albüm(ler) seçtiğinizden emin olun.", + "home_page_locked_error_local": "Yerel varlıklar kilitli klasöre taşınamıyor, atlanıyor", + "home_page_locked_error_partner": "Ortak varlıklar kilitli klasöre taşınamıyor, atlanıyor", "home_page_share_err_local": "Yerel öğeler bağlantı ile paylaşılamaz, atlanıyor", "home_page_upload_err_limit": "Aynı anda en fazla 30 öğe yüklenebilir, atlanabilir", + "host": "Ana bilgisayar", "hour": "Saat", + "id": "ID", "ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say", "ignore_icloud_photos_description": "iCloud'a yüklenmiş fotoğraflar Immich sunucusuna yüklenmesin", "image": "Resim", @@ -1057,6 +1097,12 @@ "invalid_date_format": "Geçersiz tarih formatı", "invite_people": "Kişileri Davet Et", "invite_to_album": "Albüme davet et", + "ios_debug_info_fetch_ran_at": "Veri çekme {dateTime} tarihinde çalıştırıldı", + "ios_debug_info_last_sync_at": "Son eşleme: {dateTime}", + "ios_debug_info_no_processes_queued": "Hiçbir arka plan işlemi kuyruğa alınmadı", + "ios_debug_info_no_sync_yet": "Henüz hiçbir arka plan eşleme görevi çalıştırılmadı", + "ios_debug_info_processes_queued": "{count, plural, one {{count} arka plan işlemi kuyruğa alındı} other {{count} arka plan işlemi kuyruğa alındı}}", + "ios_debug_info_processing_ran_at": "İşleme {dateTime} tarihinde çalıştırıldı", "items_count": "{count, plural, one {# Öğe} other {# Öğe}}", "jobs": "Görevler", "keep": "Koru", @@ -1065,6 +1111,9 @@ "kept_this_deleted_others": "Bu varlık tutuldu ve {count, plural, one {# varlık} other {# varlık}} silindi", "keyboard_shortcuts": "Klavye kısayolları", "language": "Dil", + "language_no_results_subtitle": "Arama teriminizi değiştirmeyi deneyin", + "language_no_results_title": "Dil bulunamadı", + "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", "last_seen": "Son görülme", "latest_version": "En son versiyon", @@ -1081,6 +1130,7 @@ "library_page_sort_created": "Oluşturma tarihi", "library_page_sort_last_modified": "Son düzenleme", "library_page_sort_title": "Albüm başlığı", + "licenses": "Lisanslar", "light": "Açık", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", @@ -1090,17 +1140,21 @@ "list": "Liste", "loading": "Yükleniyor", "loading_search_results_failed": "Arama sonuçları yüklenemedi", + "local_asset_cast_failed": "Sunucuya yüklenmemiş bir varlık yansıtılamaz", "local_network": "Yerel Wi-Fi", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", - "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır.", + "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır", "location_picker_choose_on_map": "Haritada seç", "location_picker_latitude_error": "Geçerli bir enlem yazın", "location_picker_latitude_hint": "Buraya enlem yazın", "location_picker_longitude_error": "Geçerli bir boylam yazın", "location_picker_longitude_hint": "Buraya boylam yazın", + "lock": "Kilitle", + "locked_folder": "Kilitli Klasör", "log_out": "Oturumu kapat", "log_out_all_devices": "Tüm Cihazlarda Oturumu Kapat", + "logged_in_as": "{user} olarak oturum açıldı", "logged_out_all_devices": "Tüm cihazlarda oturum kapatıldı", "logged_out_device": "Oturum kapatılmış cihaz", "login": "Giriş yap", @@ -1124,7 +1178,7 @@ "login_form_server_empty": "Sunucu URL'si girin", "login_form_server_error": "Sunucuya bağlanılamadı.", "login_has_been_disabled": "Giriş devre dışı bırakıldı.", - "login_password_changed_error": "Parola güncellenirken bir hata oluştu.", + "login_password_changed_error": "Parolanız güncellenirken bir hata oluştu.", "login_password_changed_success": "Parola güncellendi", "logout_all_device_confirmation": "Tüm cihazlarda oturum kapatmak istediğinizden emin misiniz?", "logout_this_device_confirmation": "Bu cihazda oturum kapatmak istediğinizden emin misiniz?", @@ -1133,6 +1187,7 @@ "loop_videos": "Videoları döngüye al", "loop_videos_description": "Ayrıntı görünümünde videoların otomatik döngüye alınmasını etkinleştir.", "main_branch_warning": "Geliştirme sürümü kullanıyorsunuz. Yayınlanan bir sürüm kullanmanızı önemle tavsiye ederiz!", + "main_menu": "Ana menü", "make": "Marka", "manage_shared_links": "Paylaşılan bağlantıları yönet", "manage_sharing_with_partners": "Ortaklarla paylaşımı yönet", @@ -1163,9 +1218,12 @@ "map_settings_dialog_title": "Harita Ayarları", "map_settings_include_show_archived": "Arşivdekileri dahil et", "map_settings_include_show_partners": "Partnerleri Dahil Et", - "map_settings_only_show_favorites": "Sadece Favorileri Göster", + "map_settings_only_show_favorites": "Sadece Gözdeleri Göster", "map_settings_theme_settings": "Harita Teması", "map_zoom_to_see_photos": "Fotoğrafları görmek için uzaklaştırın", + "mark_all_as_read": "Tümünü okundu olarak işaretle", + "mark_as_read": "Okundu olarak işaretle", + "marked_all_as_read": "Tümü okundu olarak işaretlendi", "matches": "Eşleşenler", "media_type": "Medya türü", "memories": "Anılar", @@ -1186,8 +1244,16 @@ "minimize": "Küçült", "minute": "Dakika", "missing": "Eksik", + "model": "Model", "month": "Ay", + "monthly_title_text_date_format": "MMMM y", "more": "Daha fazla", + "move": "Taşı", + "move_off_locked_folder": "Kilitli klasörden taşı", + "move_to_locked_folder": "Kilitli klasöre taşı", + "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tüm albümlerden kaldırılacak ve yalnızca kilitli klasörden görüntülenebilecektir", + "moved_to_archive": "{count, plural, one {# öğe arşive taşındı} other {# öğe arşive taşındı}}", + "moved_to_library": "{count, plural, one {# öğe kitaplığa taşındı} other {# öğe kitaplığa taşındı}}", "moved_to_trash": "Çöp kutusuna taşındı", "multiselect_grid_edit_date_time_err_read_only": "Salt okunur öğelerin tarihi düzenlenemedi, atlanıyor", "multiselect_grid_edit_gps_err_read_only": "Salt okunur öğelerin konumu düzenlenemedi, atlanıyor", @@ -1203,6 +1269,7 @@ "new_password": "Yeni şifre", "new_person": "Yeni kişi", "new_pin_code": "Yeni PIN kodu", + "new_pin_code_subtitle": "Kilitli klasöre ilk kez erişiyorsunuz. Bu sayfaya güvenli erişim için bir PIN kodu oluşturun", "new_user_created": "Yeni kullanıcı oluşturuldu", "new_version_available": "YENİ VERSİYON MEVCUT", "newest_first": "Önce en yeniler", @@ -1215,19 +1282,25 @@ "no_archived_assets_message": "Fotoğraf görünümünüzden kaldırmak için fotoğrafları ve videoları arşivleyin", "no_assets_message": "İLK FOTOĞRAFINIZI YÜKLEMEK İÇİN TIKLAYIN", "no_assets_to_show": "Gösterilecek öğe yok", + "no_cast_devices_found": "Yansıtılacak cihaz bulunamadı", "no_duplicates_found": "Çift bulunamadı.", "no_exif_info_available": "EXIF bilgisi mevcut değil", "no_explore_results_message": "Koleksiyonunuzu keşfetmek için daha fazla fotoğraf yükleyin.", - "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için favoriler ekleyin", + "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için gözdelere ekleyin", "no_libraries_message": "Fotoğraf ve videolarınızı görmek için bir harici kütüphane oluşturun", + "no_locked_photos_message": "Kilitli klasördeki fotoğraf ve videolar gizlidir; kitaplığınızda gezinirken veya arama yaparken görünmezler.", "no_name": "İsim yok", + "no_notifications": "Bildirim yok", + "no_people_found": "Eşleşen kişi bulunamadı", "no_places": "Yer yok", "no_results": "Sonuç bulunamadı", "no_results_description": "Eş anlamlı ya da daha genel anlamlı bir kelime deneyin", "no_shared_albums_message": "Fotoğrafları ve videoları ağınızdaki kişilerle paylaşmak için bir albüm oluşturun", "not_in_any_album": "Hiçbir albümde değil", + "not_selected": "Seçilmedi", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha önce yüklenen varlıklar için bir depolama yolu etiketi uygulamak üzere şunu başlatın", "notes": "Notlar", + "nothing_here_yet": "Burada henüz bir şey yok", "notification_permission_dialog_content": "Bildirimleri etkinleştirmek için cihaz ayarlarına gidin ve izin verin.", "notification_permission_list_tile_content": "Bildirimleri etkinleştirmek için izin verin.", "notification_permission_list_tile_enable_button": "Bildirimleri Etkinleştir", @@ -1235,17 +1308,22 @@ "notification_toggle_setting_description": "E-posta bildirimlerine izin ver", "notifications": "Bildirimler", "notifications_setting_description": "Bildirimleri yönetin", + "oauth": "OAuth", "official_immich_resources": "Resmi Immich Kaynakları", "offline": "Çevrim dışı", "ok": "Tamam", "oldest_first": "Eski olan önce", "on_this_device": "Bu cihazda", "onboarding": "Uyum Süreci", - "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve yönetim ayarlarından herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_locale_description": "Tercih ettiğiniz dili seçin. Bu ayarı daha sonra değiştirebilirsiniz.", + "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve ayarlardan herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_server_welcome_description": "Örneğinizi bazı yaygın ayarlarla ayarlayalım.", "onboarding_theme_description": "İnstance’ınız için bir renk teması seçin. Bunu daha sonra ayarlarınızdan değiştirebilirsiniz.", + "onboarding_user_welcome_description": "Haydi başlayalım!", "onboarding_welcome_user": "Hoş geldin, {user}", "online": "Çevrimiçi", - "only_favorites": "Sadece favoriler", + "only_favorites": "Sadece gözdeler", + "open": "Aç", "open_in_map_view": "Harita görünümünde aç", "open_in_openstreetmap": "OpenStreetMap'te Aç", "open_the_search_filters": "Arama filtrelerini aç", @@ -1268,7 +1346,7 @@ "partner_page_no_more_users": "Eklenecek başka kullanıcı yok", "partner_page_partner_add_failed": "Partner eklenemedi", "partner_page_select_partner": "Partner seç", - "partner_page_shared_to_title": "Paylaşıldı:", + "partner_page_shared_to_title": "Paylaşıldı", "partner_page_stop_sharing_content": "{partner} artık fotoğraflarınıza erişemeyecek.", "partner_sharing": "Ortak paylaşımı", "partners": "Ortaklar", @@ -1298,15 +1376,18 @@ "permanently_delete_assets_prompt": "Bu {count, plural, one {dosyayı} other {# dosyaları}} kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu dosyayı} other {bu dosyaları}} albümlerinizden de kaldırır.", "permanently_deleted_asset": "Kalıcı olarak silinmiş ögeler", "permanently_deleted_assets_count": "{count, plural, one {# dosya} other {# dosya}} kalıcı olarak silindi", + "permission": "İzin", + "permission_empty": "İzniniz boş olmamalı", "permission_onboarding_back": "Geri", "permission_onboarding_continue_anyway": "Yine de devam et", "permission_onboarding_get_started": "Haydi başlayalım", "permission_onboarding_go_to_settings": "Ayarlara git", "permission_onboarding_permission_denied": "İzin reddedildi. Immich'i kullanmak için Ayarlar'da fotoğraf ve video izinlerini verin.", - "permission_onboarding_permission_granted": "İzin verildi. Artık hazırsınız!", + "permission_onboarding_permission_granted": "İzin verildi! Artık hazırsınız.", "permission_onboarding_permission_limited": "Sınırlı izin. Immich'in tüm fotoğrav ve videolarınızı yedeklemesine ve yönetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.", "permission_onboarding_request": "Immich'in fotoğraflarınızı ve videolarınızı görüntüleyebilmesi için izne ihtiyacı var.", "person": "Kişi", + "person_birthdate": "{date} tarihinde doğdu", "person_hidden": "{name}{hidden, select, true { (gizli)} other {}}", "photo_shared_all_users": "Fotoğraflarınızı tüm kullanıcılarla paylaştınız gibi görünüyor veya paylaşacak kullanıcı bulunmuyor.", "photos": "Fotoğraflar", @@ -1317,6 +1398,7 @@ "pin_code_changed_successfully": "PIN kodu başarıyla değiştirildi", "pin_code_reset_successfully": "PIN kodu başarıyla sıfırlandı", "pin_code_setup_successfully": "PIN kodu başarıyla ayarlandı", + "pin_verification": "PIN kodu doğrulama", "place": "Konum", "places": "Konumlar", "places_count": "{count, plural, one {{count, number} yer} other {{count, number} yer}}", @@ -1324,19 +1406,26 @@ "play_memories": "Anıları oynat", "play_motion_photo": "Hareketli fotoğrafı oynat", "play_or_pause_video": "Videoyu oynat ya da durdur", + "please_auth_to_access": "Erişim için lütfen kimliğinizi doğrulayın", + "port": "Port", "preferences_settings_subtitle": "Uygulama tercihlerini düzenle", "preferences_settings_title": "Tercihler", "preset": "Ön ayar", "preview": "Önizleme", "previous": "Önceki", "previous_memory": "Önceki anı", - "previous_or_next_photo": "Önceki ya da sonraki fotoğraf", + "previous_or_next_day": "Gün ileri/geri", + "previous_or_next_month": "Ay ileri/geri", + "previous_or_next_photo": "Fotoğraf ileri/geri", + "previous_or_next_year": "Yıl ileri/geri", "primary": "Birincil", "privacy": "Gizlilik", + "profile": "Profil", "profile_drawer_app_logs": "Günlükler", "profile_drawer_client_out_of_date_major": "Mobil uygulama güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_client_out_of_date_minor": "Mobil uygulama güncel değil. Lütfen en son sürüme güncelleyin.", "profile_drawer_client_server_up_to_date": "Uygulama ve sunucu güncel", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Sunucu güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_server_out_of_date_minor": "Sunucu güncel değil. Lütfen en son sürüme güncelleyin.", "profile_image_of_user": "{user} kullanıcısının profil resmi", @@ -1363,7 +1452,7 @@ "purchase_lifetime_description": "Ömür boyu geçerli", "purchase_option_title": "SATIN ALMA SEÇENEKLERİ", "purchase_panel_info_1": "Immich'in gelişimi zaman ve çaba gerektiriyor ve tam zamanlı geliştiricilerimiz var. Amacımız, açık kaynak yazılımı sürdürülebilir bir gelir kaynağı haline getirmek.", - "purchase_panel_info_2": "Bu satın alma işlemi Immich'te ek işlevsellik açmayacak. Immich'in gelişimini desteklemek için size güveniyoruz.", + "purchase_panel_info_2": "Ücretli özellikler (paywall) eklememeye kararlı olduğumuz için, bu satın alma işlemi Immich'te ek işlevsellik sağlamaz. Immich'in sürekli gelişimini desteklemek için sizin gibi kullanıcılara güveniyoruz.", "purchase_panel_title": "Projeyi destekleyin", "purchase_per_server": "Sunucu başına", "purchase_per_user": "Kullanıcı başına", @@ -1390,6 +1479,8 @@ "recent_searches": "Son aramalar", "recently_added": "Son eklenenler", "recently_added_page_title": "Son Eklenenler", + "recently_taken": "Son çekilenler", + "recently_taken_page_title": "Son Çekilenler", "refresh": "Yenile", "refresh_encoded_videos": "Kodlanmış videoları yenile", "refresh_faces": "Yüzleri yenile", @@ -1408,14 +1499,21 @@ "remove_custom_date_range": "Özel tarih aralığını kaldır", "remove_deleted_assets": "Çevrimdışı dosyaları kaldır", "remove_from_album": "Albümden çıkar", - "remove_from_favorites": "Favorilerden çıkar", + "remove_from_album_action_prompt": "{count} albümden kaldırıldı", + "remove_from_favorites": "Gözdelerden çıkar", + "remove_from_lock_folder_action_prompt": "{count} kilitli klasörden kaldırıldı", + "remove_from_locked_folder": "Kilitli klasörden kaldır", + "remove_from_locked_folder_confirmation": "Bu fotoğraf ve videoları kilitli klasörden çıkarmak istediğinizden emin misiniz? Çıkarıldıklarında kitaplığınızda görünür olacaklar.", "remove_from_shared_link": "Paylaşılan bağlantıdan çıkar", + "remove_memory": "Anıyı kaldır", + "remove_photo_from_memory": "Bu anıdan fotoğrafı kaldır", + "remove_tag": "Etiketi kaldır", "remove_url": "Bağlantıyı kaldır", "remove_user": "Kullanıcıyı çıkar", "removed_api_key": "API anahtarı {name} kaldırıldı", "removed_from_archive": "Arşivden çıkarıldı", - "removed_from_favorites": "Favorilerden kaldırıldı", - "removed_from_favorites_count": "{count, plural, other {#}} favorilerden çıkarıldı", + "removed_from_favorites": "Gözdelerden kaldırıldı", + "removed_from_favorites_count": "{count, plural, other {#}} gözdelerden çıkarıldı", "removed_memory": "Anı kaldırıldı", "removed_photo_from_memory": "Fotoğraf anıdan kaldırıldı", "removed_tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketleri kaldırıldı", @@ -1460,7 +1558,7 @@ "search_by_context": "Bağlama göre ara", "search_by_description": "Açıklamaya göre ara", "search_by_description_example": "Sapa'da yürüyüş günü", - "search_by_filename": "Dosya adına göre ara", + "search_by_filename": "Dosya adına veya uzantısına göre ara", "search_by_filename_example": "Örn. IMG_1234.JPG veya PNG", "search_camera_make": "Kamera markasına göre ara...", "search_camera_model": "Kamera modeline göre ara...", @@ -1473,6 +1571,7 @@ "search_filter_date_title": "Tarih aralığı seç", "search_filter_display_option_not_in_album": "Albümde değil", "search_filter_display_options": "Görüntü Seçenekleri", + "search_filter_filename": "Dosya adına göre ara", "search_filter_location": "Konum", "search_filter_location_title": "Konum seç", "search_filter_media_type": "Medya Türü", @@ -1480,8 +1579,10 @@ "search_filter_people_title": "Kişi seç", "search_for": "Araştır", "search_for_existing_person": "Mevcut bir kişiyi ara", + "search_no_more_result": "Daha fazla sonuç yok", "search_no_people": "Kişi yok", "search_no_people_named": "\"{name}\" isimli bir kişi yok", + "search_no_result": "Sonuç bulunamadı. Farklı bir arama terimi veya kombinasyon deneyin", "search_options": "Arama seçenekleri", "search_page_categories": "Kategoriler", "search_page_motion_photos": "Canlı Fotoğraflar", @@ -1509,9 +1610,11 @@ "searching_locales": "Yerleri arıyor...", "second": "Saniye", "see_all_people": "Tüm kişileri gör", + "select": "Seç", "select_album_cover": "Albüm kapağı seç", "select_all": "Tümünü seç", "select_all_duplicates": "Tüm çiftleri seç", + "select_all_in": "{group} içindekilerin tümünü seç", "select_avatar_color": "Avatar rengini seç", "select_face": "Yüzü seç", "select_featured_photo": "Öne çıkan fotoğrafı seç", @@ -1519,6 +1622,7 @@ "select_keep_all": "Hepsini sakla", "select_library_owner": "Kütüphane sahibini seç", "select_new_face": "Yeni yüz seç", + "select_person_to_tag": "Etiketlemek için bir kişi seçin", "select_photos": "Fotoğrafları seç", "select_trash_all": "Hepsini çöpe at", "select_user_for_sharing_page_err_album": "Albüm oluşturulamadı", @@ -1531,6 +1635,7 @@ "server_info_box_server_url": "Sunucu URL", "server_offline": "Sunucu çevrimdışı", "server_online": "Sunucu çevrimiçi", + "server_privacy": "Sunucu Gizliliği", "server_stats": "Sunucu istatistikleri", "server_version": "Sunucu versiyonu", "set": "Ayarla", @@ -1540,6 +1645,7 @@ "set_date_of_birth": "Doğum tarihini ayarla", "set_profile_picture": "Profil resmini ayarla", "set_slideshow_to_fullscreen": "Slayt gösterisini tam ekran yap", + "set_stack_primary_asset": "Birincil varlık olarak ayarla", "setting_image_viewer_help": "Görüntüleyici önce küçük resmi gösterir, ardından orta boy önizlemeyi (etkinleştirilmişse) ve son olarak orijinali (etkinleştirilmişse) gösterir.", "setting_image_viewer_original_subtitle": "Orijinal tam çözünürlüklü görüntüyü göstermek için etkinleştirin. Veri kullanımını azaltmak için devre dışı bırakın (hem ağ hem de cihaz önbelleği).", "setting_image_viewer_original_title": "Orijinal görüntüyü göster", @@ -1560,6 +1666,8 @@ "setting_notifications_total_progress_subtitle": "Toplam yükleme ilerlemesi (tamamlanan/toplam)", "setting_notifications_total_progress_title": "Arkaplan yedeklemesi toplam ilerlemesini göster", "setting_video_viewer_looping_title": "Döngü", + "setting_video_viewer_original_video_subtitle": "Sunucudan video aktarılırken, transcode (dönüştürülmüş) sürüm mevcut olsa bile orijinal dosya oynatılır. Bu durum, arabelleğe alma (buffering) sorunlarına yol açabilir. Videolar yerel olarak mevcutsa, bu ayardan bağımsız olarak orijinal kalitede oynatılır.", + "setting_video_viewer_original_video_title": "Orijinal videoyu zorla", "settings": "Ayarlar", "settings_require_restart": "Bu ayarı uygulamak için lütfen Immich'i yeniden başlatın", "settings_saved": "Ayarlar kaydedildi", @@ -1568,6 +1676,7 @@ "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "Hazırlanıyor...", + "share_link": "Bağlantıyı Paylaş", "shared": "Paylaşılan", "shared_album_activities_input_disable": "Yoruma kapalı", "shared_album_activity_remove_content": "Bu etkinliği silmek istiyor musunuz?", @@ -1580,6 +1689,7 @@ "shared_by_user": "{user} tarafından paylaşıldı", "shared_by_you": "Senin tarafından paylaşıldı", "shared_from_partner": "{partner} tarafından paylaşılan fotoğraflar", + "shared_intent_upload_button_progress_text": "{current} / {total} Yüklendi", "shared_link_app_bar_title": "Paylaşılan Bağlantılar", "shared_link_clipboard_copied_massage": "Panoya kopyalandı", "shared_link_clipboard_text": "Bağlantı: {link}\nParola: {password}", @@ -1606,6 +1716,7 @@ "shared_link_expires_second": "Süresi {count} saniye içinde doluyor", "shared_link_expires_seconds": "{count} sanyei içinde süresi doluyor", "shared_link_individual_shared": "Bireysel paylaşımlı", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Paylaşılan Bağlantıları Yönet", "shared_link_options": "Paylaşılan bağlantı seçenekleri", "shared_links": "Paylaşılan bağlantılar", @@ -1672,12 +1783,14 @@ "start_date": "Başlangıç tarihi", "state": "Eyalet/İl", "status": "Durum", + "stop_casting": "Yansıtmayı durdur", "stop_motion_photo": "Hareketli fotoğrafı durdur", "stop_photo_sharing": "Fotoğraflarınızı paylaşmayı durdurmak mı istiyorsunuz?", "stop_photo_sharing_description": "{partner} artık fotoğraflarınıza erişemeyecek.", "stop_sharing_photos_with_user": "Bu kullanıcı ile fotoğraflarınızı paylaşmayı durdurun", "storage": "Depolama alanı", "storage_label": "Depolama yolu", + "storage_quota": "Depolama Kotası", "storage_usage": "{used} / {available} kullanıldı", "submit": "Gönder", "suggestions": "Öneriler", @@ -1714,7 +1827,7 @@ "theme_setting_system_primary_color_title": "Sistem rengini kullan", "theme_setting_system_theme_switch": "Otomatik (sistem ayarına göre)", "theme_setting_theme_subtitle": "Uygulama teması seç", - "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", + "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme, yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", "theme_setting_three_stage_loading_title": "Üç aşamalı yüklemeyi etkinleştir", "they_will_be_merged_together": "Birlikte birleştirilecekler", "third_party_resources": "Üçüncü taraf kaynaklar", @@ -1723,7 +1836,7 @@ "timezone": "Zaman dilimi", "to_archive": "Arşivle", "to_change_password": "Şifreyi değiştir", - "to_favorite": "Favorilere ekle", + "to_favorite": "Gözdelere ekle", "to_login": "Oturum aç", "to_parent": "Üst öğeye git", "to_trash": "Çöpe taşı", @@ -1749,7 +1862,8 @@ "unable_to_setup_pin_code": "PIN kodu ayarlanamadı", "unarchive": "Arşivden çıkar", "unarchived_count": "{count, plural, other {# arşivden çıkarıldı}}", - "unfavorite": "Favorilerden kaldır", + "undo": "Geri al", + "unfavorite": "Gözdelerden kaldır", "unhide_person": "Kişiyi göster", "unknown": "Bilinmeyen", "unknown_country": "Bilinmeyen ülke", @@ -1765,9 +1879,11 @@ "unsaved_change": "Kaydedilmemiş değişiklik", "unselect_all": "Tümünü seçimini kaldır", "unselect_all_duplicates": "Tüm çiftlerin seçimini kaldır", + "unselect_all_in": "{group} içindeki tüm seçimleri kaldır", "unstack": "Yığını kaldır", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığını kaldırıldı", "up_next": "Sıradaki", + "updated_at": "Güncellenme", "updated_password": "Şifreyi güncelle", "upload": "Yükle", "upload_concurrency": "Yükleme eşzamanlılığı", @@ -1780,14 +1896,20 @@ "upload_status_errors": "Hatalar", "upload_status_uploaded": "Yüklendi", "upload_success": "Yükleme başarılı, yüklenen yeni ögeleri görebilmek için sayfayı yenileyin.", + "upload_to_immich": "Immich'e Yükle ({count})", + "uploading": "Yükleniyor", + "url": "URL", "usage": "Kullanım", + "use_biometric": "Biyometri kullan", "use_current_connection": "mevcut bağlantıyı kullan", "use_custom_date_range": "Bunun yerine özel tarih aralığını kullan", "user": "Kullanıcı", + "user_has_been_deleted": "Bu kullanıcı silindi.", "user_id": "Kullanıcı ID", "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu dosya} other {Bu}} {user} tarafından beğenildi", "user_pin_code_settings": "PIN Kodu", "user_pin_code_settings_description": "PIN kodunuzu yönetin", + "user_privacy": "Kullanıcı Gizliliği", "user_purchase_settings": "Satın Alma", "user_purchase_settings_description": "Satın alma işlemlerini yönet", "user_role_set": "{user}, {role} olarak ayarlandı", @@ -1805,6 +1927,7 @@ "version_announcement_message": "Merhaba! Immich'in yeni bir sürümü mevcut. Lütfen yapılandırmanızın güncel olduğundan emin olmak için sürüm notlarını okumak için biraz zaman ayırın, özellikle WatchTower veya Immich kurulumunuzu otomatik olarak güncelleyen bir mekanizma kullanıyorsanız yanlış yapılandırmaların önüne geçmek adına bu önemlidir.", "version_history": "Versiyon geçmişi", "version_history_item": "{version}, {date} tarihinde kuruldu", + "video": "Video", "video_hover_setting": "Üzerinde durulduğunda video önizlemesi oynat", "video_hover_setting_description": "Öğe üzerinde fareyle durulduğunda video küçük resmini oynatır. Bu özellik devre dışıyken, oynatma simgesine fareyle gidilerek oynatma başlatılabilir.", "videos": "Videolar", @@ -1819,7 +1942,9 @@ "view_name": "Göster", "view_next_asset": "Sonraki dosyayı görüntüle", "view_previous_asset": "Önceki dosyayı görüntüle", + "view_qr_code": "QR kodu görüntüle", "view_stack": "Yığını görüntüle", + "view_user": "Kullanıcıyı Görüntüle", "viewer_remove_from_stack": "Yığından Kaldır", "viewer_stack_use_as_main_asset": "Ana fotoğraf olarak kullan", "viewer_unstack": "Yığını Kaldır", @@ -1830,6 +1955,7 @@ "welcome": "Hoş geldiniz", "welcome_to_immich": "Immich'e hoş geldiniz", "wifi_name": "Wi-Fi Adı", + "wrong_pin_code": "Yanlış PIN kodu", "year": "Yıl", "years_ago": "{years, plural, one {bir yıl} other {# yıl}} önce", "yes": "Evet", diff --git a/i18n/uk.json b/i18n/uk.json index 7b843ceff9..8a375e627a 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -244,6 +244,7 @@ "storage_template_migration_info": "Шаблон зберігання конвертуватиме всі розширення у нижній регістр. Зміни шаблону застосовуватимуться лише до нових ресурсів. Щоб застосувати шаблон до раніше завантажених ресурсів, запустіть {job}.", "storage_template_migration_job": "Завдання міграції шаблону зберігання", "storage_template_more_details": "Для отримання детальнішої інформації про цю функцію, звертайтесь до Шаблону зберігання та його наслідків", + "storage_template_onboarding_description_v2": "Якщо цю функцію увімкнено, файли будуть автоматично впорядковуватися за шаблоном, визначеним користувачем. Докладніше дивіться в документації.", "storage_template_path_length": "Приблизна максимальна довжина шляху: {length, number}/{limit, number}", "storage_template_settings": "Шаблон сховища", "storage_template_settings_description": "Керуйте структурою тек та іменем завантаженого файлу", @@ -463,7 +464,6 @@ "assets": "елементи", "assets_added_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_added_to_album_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}} до альбому", - "assets_added_to_name_count": "Додано {count, plural, one {# елемент} other {# елементів}} до {hasName, select, true {{name}} other {нового альбому}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Ресурс} other {Ресурси}} не можна додати до альбому", "assets_count": "{count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_deleted_permanently": "{count} елемент(и) остаточно видалено", @@ -702,7 +702,6 @@ "daily_title_text_date": "Е, МММ дд", "daily_title_text_date_year": "Е, МММ дд, рррр", "dark": "Темний", - "darkTheme": "Перемкнути темну тему", "date_after": "Дата після", "date_and_time": "Дата і час", "date_before": "Дата до", diff --git a/i18n/vi.json b/i18n/vi.json index 5890ef87db..10c883dc70 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -461,7 +461,6 @@ "assets": "Các tập tin", "assets_added_count": "Đã thêm {count, plural, one {# mục} other {# mục}}", "assets_added_to_album_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào album", - "assets_added_to_name_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào {hasName, select, true {{name}} other {album mới}}", "assets_count": "{count, plural, one {# mục} other {# mục}}", "assets_deleted_permanently": "Đã xoá vĩnh viễn {count} mục", "assets_deleted_permanently_from_server": "Đã xoá vĩnh viễn {count} mục khỏi máy chủ Immich", @@ -534,7 +533,7 @@ "backup_controller_page_start_backup": "Bắt đầu sao lưu", "backup_controller_page_status_off": "Sao lưu tự động khi ứng dụng hoạt động đang tắt", "backup_controller_page_status_on": "Sao lưu tự động khi ứng dụng hoạt động đang bật", - "backup_controller_page_storage_format": "Đã sử dụng {used} của {total}", + "backup_controller_page_storage_format": "Đã dùng {used} của {total}", "backup_controller_page_to_backup": "Các album cần được sao lưu", "backup_controller_page_total_sub": "Tất cả ảnh và video không trùng lập từ các album được chọn", "backup_controller_page_turn_off": "Tắt sao lưu khi ứng dụng hoạt động", @@ -698,7 +697,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tối", - "darkTheme": "Chuyển đổi chủ đề tối", "date_after": "Ngày sau", "date_and_time": "Ngày và giờ", "date_before": "Ngày trước", @@ -1016,7 +1014,7 @@ "group_year": "Nhóm theo năm", "haptic_feedback_switch": "Bật phản hồi haptic", "haptic_feedback_title": "Phản hồi Hapic", - "has_quota": "Có hạn mức", + "has_quota": "Hạn mức", "header_settings_add_header_tip": "Thêm Header", "header_settings_field_validator_msg": "Trường này không được để trống", "header_settings_header_name_input": "Tên Header", @@ -1426,7 +1424,7 @@ "purchase_button_activate": "Kích hoạt", "purchase_button_buy": "Mua", "purchase_button_buy_immich": "Mua Immich", - "purchase_button_never_show_again": "Không hiển thị lại", + "purchase_button_never_show_again": "Không hiện lại", "purchase_button_reminder": "Nhắc tôi trong 30 ngày", "purchase_button_remove_key": "Xóa khóa", "purchase_button_select": "Chọn", @@ -1504,7 +1502,7 @@ "rename": "Đổi tên", "repair": "Sửa chữa", "repair_no_results_message": "Các tập tin không được theo dõi và bị mất sẽ xuất hiện ở đây", - "replace_with_upload": "Thay thế bằng tập tin tải lên", + "replace_with_upload": "Thay thế tập tin khác", "repository": "Kho lưu trữ", "require_password": "Yêu cầu mật khẩu", "require_user_to_change_password_on_first_login": "Yêu cầu người dùng thay đổi mật khẩu ở lần đầu đăng nhập", @@ -1522,7 +1520,7 @@ "restored_asset": "Ảnh đã được khôi phục", "resume": "Tiếp tục", "retry_upload": "Thử tải lên lại", - "review_duplicates": "Xem xét các mục trùng lặp", + "review_duplicates": "Xem lại các mục trùng lặp", "role": "Vai trò", "role_editor": "Người chỉnh sửa", "role_viewer": "Người xem", @@ -1617,7 +1615,7 @@ "server_info_box_app_version": "Phiên bản ứng dụng", "server_info_box_server_url": "Địa chỉ máy chủ", "server_offline": "Máy chủ ngoại tuyến", - "server_online": "Máy chủ trực tuyến", + "server_online": "Phiên bản", "server_privacy": "Quyền riêng tư máy chủ", "server_stats": "Thống kê máy chủ", "server_version": "Phiên bản máy chủ", @@ -1772,7 +1770,7 @@ "storage": "Bộ nhớ", "storage_label": "Nhãn lưu trữ", "storage_quota": "Giới hạn Dung lượng", - "storage_usage": "Đã sử dụng {used} của {available}", + "storage_usage": "Đã dùng {used} của {available}", "submit": "Gửi", "suggestions": "Gợi ý", "sunrise_on_the_beach": "Bình minh trên bãi biển", @@ -1819,7 +1817,7 @@ "to_change_password": "Đổi mật khẩu", "to_favorite": "Yêu thích", "to_login": "Đăng nhập", - "to_parent": "Đi tới thư mục cha", + "to_parent": "Về thư mục gốc", "to_trash": "Xóa", "toggle_settings": "Chuyển đổi cài đặt", "total": "Tổng cộng", @@ -1894,7 +1892,7 @@ "user_purchase_settings_description": "Quản lý mục mua của bạn", "user_role_set": "Đặt {user} làm {role}", "user_usage_detail": "Chi tiết sử dụng của người dùng", - "user_usage_stats": "Thống kê sử dụng của tài khoản", + "user_usage_stats": "Thống kê sử dụng tài khoản", "user_usage_stats_description": "Xem thống kê sử dụng của tài khoản", "username": "Tên người dùng", "users": "Người dùng", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index e5a8a65e25..334da7bfb1 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -464,7 +464,6 @@ "assets": "項目", "assets_added_count": "已新增 {count, plural, one {# 個項目} other {# 個項目}}", "assets_added_to_album_count": "已將 {count, plural, other {# 個項目}}加入相簿", - "assets_added_to_name_count": "已將 {count, plural, other {# 個項目}}加入{hasName, select, true {{name}} other {新相簿}}", "assets_cannot_be_added_to_album_count": "{count. plural, one {個} other {個}} 項目未能被添加至相簿", "assets_count": "{count, plural, one {# 個項目} other {# 個項目}}", "assets_deleted_permanently": "{count} 個項目已被永久刪除", @@ -699,7 +698,6 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "切換爲深色主題", "date_after": "日期之後", "date_and_time": "日期與時間", "date_before": "日期之前", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 17c6954ab7..28d6a5c0d5 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -166,6 +166,20 @@ "metadata_settings_description": "管理元数据设置", "migration_job": "迁移", "migration_job_description": "将项目和人脸识别的缩略图迁移到最新的文件夹结构", + "nightly_tasks_cluster_faces_setting_description": "对新检测到的面部进行面部识别", + "nightly_tasks_cluster_new_faces_setting": "群组新面孔", + "nightly_tasks_database_cleanup_setting": "数据库清理任务", + "nightly_tasks_database_cleanup_setting_description": "清理数据库中过期的旧数据", + "nightly_tasks_generate_memories_setting": "生成回忆", + "nightly_tasks_generate_memories_setting_description": "从项目中生成新的回忆", + "nightly_tasks_missing_thumbnails_setting": "生成缺失的缩略图", + "nightly_tasks_missing_thumbnails_setting_description": "为生成缩略图队列无缩略图的项目", + "nightly_tasks_settings": "夜间任务设置", + "nightly_tasks_settings_description": "管理夜间任务", + "nightly_tasks_start_time_setting": "开始时间", + "nightly_tasks_start_time_setting_description": "服务器开始运行夜间任务的时间", + "nightly_tasks_sync_quota_usage_setting": "同步配额使用情况", + "nightly_tasks_sync_quota_usage_setting_description": "根据当前使用情况更新用户存储配额", "no_paths_added": "无已添加路径", "no_pattern_added": "无已添加规则", "note_apply_storage_label_previous_assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "移动端重定向 URI", "oauth_mobile_redirect_uri_override": "移动端重定向 URI 覆盖", "oauth_mobile_redirect_uri_override_description": "当 OAuth 提供商不允许使用移动 URI 时启用,如“''{callback}''”", + "oauth_role_claim": "角色声明", + "oauth_role_claim_description": "根据此声明的存在自动授予管理员访问权限。声明可以是“user”(用户)或“admin”(管理员)。", "oauth_settings": "OAuth", "oauth_settings_description": "管理 OAuth 登录设置", "oauth_settings_more_details": "关于此功能的更多详细信息,请查看相关文档。", @@ -357,6 +373,8 @@ "admin_password": "管理员密码", "administration": "系统管理", "advanced": "高级", + "advanced_settings_beta_timeline_subtitle": "体验全新的应用程序", + "advanced_settings_beta_timeline_title": "测试版时间线", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此选项可在同步过程中根据备用条件筛选项目。仅当您在应用程序检测所有相册均遇到问题时才尝试此功能。", "advanced_settings_enable_alternate_media_filter_title": "[实验] 使用备用的设备相册同步筛选条件", "advanced_settings_log_level_title": "日志等级: {level}", @@ -388,6 +406,7 @@ "album_options": "相册设置", "album_remove_user": "移除用户?", "album_remove_user_confirmation": "确定要移除“{user}”吗?", + "album_search_not_found": "未找到符合搜索条件的相册", "album_share_no_users": "看起来您已与所有用户共享了此相册,或者您根本没有任何用户可共享。", "album_updated": "相册有更新", "album_updated_setting_description": "当共享相册有新项目时接收邮件通知", @@ -407,6 +426,7 @@ "albums_default_sort_order": "默认相册排序方式", "albums_default_sort_order_description": "创建新相册时的项目初始排序方式。", "albums_feature_description": "可与其他用户共享的项目收藏。", + "albums_on_device_count": "设备上的相册({count} 个)", "all": "全部", "all_albums": "所有相册", "all_people": "全部人物", @@ -427,12 +447,13 @@ "app_settings": "应用设置", "appears_in": "出现于", "archive": "归档", + "archive_action_prompt": "已将 {count} 项添加到归档", "archive_or_unarchive_photo": "归档或取消归档照片", "archive_page_no_archived_assets": "未找到归档项目", "archive_page_title": "归档({count})", "archive_size": "归档大小", "archive_size_description": "配置下载归档大小(GB)", - "archived": "已存档", + "archived": "已归档", "archived_count": "{count, plural, other {已归档 # 项}}", "are_these_the_same_person": "他们是同一位人吗?", "are_you_sure_to_do_this": "确定要这样做吗?", @@ -464,7 +485,6 @@ "assets": "项目", "assets_added_count": "已添加{count, plural, one {#个项目} other {#个项目}}", "assets_added_to_album_count": "已添加{count, plural, one {#个项目} other {#个项目}}到相册", - "assets_added_to_name_count": "已添加{count, plural, one {#个项目} other {#个项目}}到{hasName, select, true {{name}} other {新相册}}", "assets_cannot_be_added_to_album_count": "无法添加 {count, plural, one {个项目} other {个项目}} 到相册中", "assets_count": "{count, plural, one {#个项目} other {#个项目}}", "assets_deleted_permanently": "{count} 个项目已被永久删除", @@ -587,6 +607,7 @@ "cancel": "取消", "cancel_search": "取消搜索", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "无法合并人物", "cannot_undo_this_action": "注意:该操作无法被撤消!", "cannot_update_the_description": "无法更新描述", @@ -703,7 +724,7 @@ "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "暗色主题", + "dark_theme": "切换深色主题", "date_after": "开始日期", "date_and_time": "日期与时间", "date_before": "结束日期", @@ -719,6 +740,7 @@ "default_locale": "默认地区", "default_locale_description": "根据您的浏览器地区设置日期和数字显示格式", "delete": "删除", + "delete_action_prompt": "已永久删除 {count} 项", "delete_album": "删除相册", "delete_api_key_prompt": "确定删除此 API 密钥吗?", "delete_dialog_alert": "这些项目将从 Immich 和您的设备中永久删除", @@ -732,6 +754,7 @@ "delete_key": "删除密钥", "delete_library": "删除图库", "delete_link": "删除链接", + "delete_local_action_prompt": "已删除本地项目{count}项", "delete_local_dialog_ok_backed_up_only": "仅删除已备份项目", "delete_local_dialog_ok_force": "确认删除", "delete_others": "删除其它", @@ -745,6 +768,7 @@ "description": "描述", "description_input_hint_text": "添加描述...", "description_input_submit_error": "更新描述时出错,请检查日志以获取更多详细信息", + "deselect_all": "取消全选", "details": "详情", "direction": "方向", "disabled": "已禁用", @@ -762,6 +786,7 @@ "documentation": "帮助文档", "done": "完成", "download": "下载", + "download_action_prompt": "正在下载 {count} 个项目", "download_canceled": "下载已取消", "download_complete": "下载完成", "download_enqueue": "已加入下载队列", @@ -799,6 +824,7 @@ "edit_key": "编辑 API 密钥", "edit_link": "编辑链接", "edit_location": "编辑位置", + "edit_location_action_prompt": "{count} 个位置已编辑", "edit_location_dialog_title": "位置", "edit_name": "编辑名称", "edit_people": "编辑人物", @@ -817,6 +843,7 @@ "empty_trash": "清空回收站", "empty_trash_confirmation": "确定要清空回收站?这将永久删除回收站中的所有项目。\n注意:该操作无法撤消!", "enable": "启用", + "enable_backup": "启用备份", "enable_biometric_auth_description": "输入您的PIN码以启用生物识别身份验证", "enabled": "已启用", "end_date": "结束日期", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "加载项目失败", "failed_to_load_folder": "加载文件夹失败", "favorite": "收藏", + "favorite_action_prompt": "已将 {count} 项添加到收藏", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏夹", "favorites_page_no_favorites": "未找到收藏项目", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "创建日期", "library_page_sort_last_modified": "上次修改", "library_page_sort_title": "相册标题", + "licenses": "许可证", "light": "浅色", "like_deleted": "已删除的收藏", "link_motion_video": "链接动态视频", @@ -1246,6 +1275,7 @@ "more": "更多", "move": "移动", "move_off_locked_folder": "移出锁定文件夹", + "move_to_lock_folder_action_prompt": "已将 {count} 项添加到锁定文件夹", "move_to_locked_folder": "移动到锁定文件夹", "move_to_locked_folder_confirmation": "这些照片和视频将从所有相册中移除,只能在锁定文件夹中查看", "moved_to_archive": "已归档 {count, plural, one {# 个项目} other {# 个项目}}", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "支持者状态", "purchase_server_title": "服务器", "purchase_settings_server_activated": "服务器产品密钥正在由管理员管理", + "queue_status": "排队中 {count}/{total}", "rating": "星级", "rating_clear": "删除星级", "rating_count": "{count, plural, one {#星} other {#星}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "取消自定义日期范围", "remove_deleted_assets": "彻底删除文件", "remove_from_album": "从相册中移除", + "remove_from_album_action_prompt": "从相册中移除了 {count} 项", "remove_from_favorites": "移出收藏", + "remove_from_lock_folder_action_prompt": "已从锁定的文件夹中移除 {count} 项", "remove_from_locked_folder": "从锁定文件夹中移除", "remove_from_locked_folder_confirmation": "您确定要将这些照片和视频移出锁定文件夹吗?移出后它们将会在您的图库中可见。", "remove_from_shared_link": "从共享链接中移除", @@ -1667,6 +1700,7 @@ "settings_saved": "设置已保存", "setup_pin_code": "设置PIN码", "share": "共享", + "share_action_prompt": "已共享 {count} 项目", "share_add_photos": "添加项目", "share_assets_selected": "{count} 已选择", "share_dialog_preparing": "正在准备...", @@ -1768,6 +1802,7 @@ "sort_title": "标题", "source": "GitHub 源代码", "stack": "堆叠", + "stack_action_prompt": "{count} 个已堆叠", "stack_duplicates": "堆叠重复项目", "stack_select_one_photo": "为堆叠选择一张展示图", "stack_selected_photos": "堆叠选定的照片", @@ -1838,6 +1873,7 @@ "total": "总计", "total_usage": "总用量", "trash": "回收站", + "trash_action_prompt": "已将 {count} 项移至回收站", "trash_all": "全部删除", "trash_count": "删除{count, number}项", "trash_delete_asset": "将项目放入回收站/删除", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "无法修改PIN码", "unable_to_setup_pin_code": "无法设置PIN码", "unarchive": "取消归档", + "unarchive_action_prompt": "已从归档中移除 {count} 项", "unarchived_count": "{count, plural, other {取消归档 # 项}}", "undo": "撤销", "unfavorite": "取消收藏", + "unfavorite_action_prompt": "已从收藏中移除 {count} 项", "unhide_person": "显示人物", "unknown": "未知", "unknown_country": "未知的国家", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "取消选择所有重复项", "unselect_all_in": "取消选择 {group} 中的所有内容", "unstack": "取消堆叠", + "unstack_action_prompt": "{count} 个未堆叠", "unstacked_assets_count": "{count, plural, one {#个项目} other {#个项目}}已取消堆叠", + "untagged": "无标签", "up_next": "下一个", "updated_at": "已更新", "updated_password": "更新密码", "upload": "上传", "upload_concurrency": "上传并发", + "upload_details": "上传详情", "upload_dialog_info": "是否要将所选项目备份到服务器?", "upload_dialog_title": "上传项目", "upload_errors": "上传完成,出现{count, plural, one {#个错误} other {#个错误}},刷新页面以查看新上传的项目。", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "查看帐户使用统计信息", "username": "用户名", "users": "用户", + "users_added_to_album_count": "已将 {count, plural, one {# 个用户} other {# 个用户}} 添加到相册", "utilities": "实用工具", "validate": "验证", "validate_endpoint_error": "请输入有效的 URL", @@ -1930,6 +1972,7 @@ "view_album": "查看相册", "view_all": "查看全部", "view_all_users": "查看全部用户", + "view_details": "查看详情", "view_in_timeline": "在时间轴中查看", "view_link": "查看链接", "view_links": "查看链接", From 166452640d6d421d4ff1281134e5cf0a39971895 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:28:15 +0100 Subject: [PATCH 027/169] chore(deps): update dependency @types/node to ^22.16.4 (#20068) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package-lock.json | 10 +++++----- cli/package.json | 2 +- e2e/package-lock.json | 12 ++++++------ e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 8 ++++---- open-api/typescript-sdk/package.json | 2 +- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 7c87c3be2e..0d3db4cffa 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -61,7 +61,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -1355,9 +1355,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/cli/package.json b/cli/package.json index e1f49475c0..3fde7e8b02 100644 --- a/cli/package.json +++ b/cli/package.json @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1e2f8b47e1..1884b22dd5 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -16,7 +16,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -68,7 +68,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -102,7 +102,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -1996,9 +1996,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 24b97bb4b7..c780a93a24 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -26,7 +26,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index ab9f3b1fd7..01086f3113 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index f2885f4a7b..c1bf8b4f65 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" }, "repository": { diff --git a/server/package-lock.json b/server/package-lock.json index 11be49ecb6..1239730fce 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -109,7 +109,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -7258,9 +7258,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" diff --git a/server/package.json b/server/package.json index c765786960..5a385193ec 100644 --- a/server/package.json +++ b/server/package.json @@ -135,7 +135,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From 496b0c7076a23a6b34e2ff7ede94ee85bafe9e1a Mon Sep 17 00:00:00 2001 From: Daimolean <92239625+wuzihao051119@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:29:14 +0800 Subject: [PATCH 028/169] fix(server): missing integer type (#20075) --- .../openapi/lib/model/sync_asset_face_v1.dart | 24 +++++++++---------- open-api/immich-openapi-specs.json | 12 +++++----- server/src/dtos/sync.dto.ts | 6 +++++ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/mobile/openapi/lib/model/sync_asset_face_v1.dart b/mobile/openapi/lib/model/sync_asset_face_v1.dart index 853a8a1514..60d1766e34 100644 --- a/mobile/openapi/lib/model/sync_asset_face_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_face_v1.dart @@ -27,19 +27,19 @@ class SyncAssetFaceV1 { String assetId; - num boundingBoxX1; + int boundingBoxX1; - num boundingBoxX2; + int boundingBoxX2; - num boundingBoxY1; + int boundingBoxY1; - num boundingBoxY2; + int boundingBoxY2; String id; - num imageHeight; + int imageHeight; - num imageWidth; + int imageWidth; String? personId; @@ -104,13 +104,13 @@ class SyncAssetFaceV1 { return SyncAssetFaceV1( assetId: mapValueOfType(json, r'assetId')!, - boundingBoxX1: num.parse('${json[r'boundingBoxX1']}'), - boundingBoxX2: num.parse('${json[r'boundingBoxX2']}'), - boundingBoxY1: num.parse('${json[r'boundingBoxY1']}'), - boundingBoxY2: num.parse('${json[r'boundingBoxY2']}'), + boundingBoxX1: mapValueOfType(json, r'boundingBoxX1')!, + boundingBoxX2: mapValueOfType(json, r'boundingBoxX2')!, + boundingBoxY1: mapValueOfType(json, r'boundingBoxY1')!, + boundingBoxY2: mapValueOfType(json, r'boundingBoxY2')!, id: mapValueOfType(json, r'id')!, - imageHeight: num.parse('${json[r'imageHeight']}'), - imageWidth: num.parse('${json[r'imageWidth']}'), + imageHeight: mapValueOfType(json, r'imageHeight')!, + imageWidth: mapValueOfType(json, r'imageWidth')!, personId: mapValueOfType(json, r'personId'), sourceType: mapValueOfType(json, r'sourceType')!, ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 2f41318d6d..4acd431203 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13805,25 +13805,25 @@ "type": "string" }, "boundingBoxX1": { - "type": "number" + "type": "integer" }, "boundingBoxX2": { - "type": "number" + "type": "integer" }, "boundingBoxY1": { - "type": "number" + "type": "integer" }, "boundingBoxY2": { - "type": "number" + "type": "integer" }, "id": { "type": "string" }, "imageHeight": { - "type": "number" + "type": "integer" }, "imageWidth": { - "type": "number" + "type": "integer" }, "personId": { "nullable": true, diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index e0c9c059c4..c8b1a7dde9 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -261,11 +261,17 @@ export class SyncAssetFaceV1 { id!: string; assetId!: string; personId!: string | null; + @ApiProperty({ type: 'integer' }) imageWidth!: number; + @ApiProperty({ type: 'integer' }) imageHeight!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX2!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY2!: number; sourceType!: string; } From bd92f6b12dbe1b2fa84b26100fe1eb03e848343a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:31:01 +0000 Subject: [PATCH 029/169] chore(deps): update dependency dotenv to v17 (#20073) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 2bd5409903..c1147447de 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -66,7 +66,7 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", "eslint-p": "^0.25.0", @@ -4698,9 +4698,9 @@ } }, "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", + "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", "dev": true, "license": "BSD-2-Clause", "engines": { diff --git a/web/package.json b/web/package.json index c63c52a916..158c490b6a 100644 --- a/web/package.json +++ b/web/package.json @@ -83,7 +83,7 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", "eslint-p": "^0.25.0", From 7b41c6348c910fd93e43bc06206145ca83accb93 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Tue, 22 Jul 2025 11:49:18 +0200 Subject: [PATCH 030/169] chore(web): update translations (#20076) Co-authored-by: Zack Pollard --- i18n/lv.json | 1 + i18n/nl.json | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/i18n/lv.json b/i18n/lv.json index 01a506a90a..5af3e0511c 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -485,6 +485,7 @@ "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "failed_to_create_album": "Neizdevās izveidot albumu", + "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "unable_to_change_description": "Neizdevās nomainīt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", "unable_to_delete_user": "Neizdevās dzēst lietotāju", diff --git a/i18n/nl.json b/i18n/nl.json index ecd8418fa5..2cedd63b15 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -406,6 +406,7 @@ "album_options": "Albumopties", "album_remove_user": "Gebruiker verwijderen?", "album_remove_user_confirmation": "Weet je zeker dat je {user} wilt verwijderen?", + "album_search_not_found": "Geen albums gevonden die aan je zoekopdracht voldoen", "album_share_no_users": "Het lijkt erop dat je dit album met alle gebruikers hebt gedeeld, of dat je geen gebruikers hebt om mee te delen.", "album_updated": "Album bijgewerkt", "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe assets heeft", @@ -605,6 +606,7 @@ "cancel": "Annuleren", "cancel_search": "Zoeken annuleren", "canceled": "Geannuleerd", + "canceling": "Annuleren", "cannot_merge_people": "Kan mensen niet samenvoegen", "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", @@ -765,6 +767,7 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", + "deselect_all": "Alles Deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", @@ -839,6 +842,7 @@ "empty_trash": "Prullenbak leegmaken", "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle assets in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", "enable": "Inschakelen", + "enable_backup": "Back-up aanzetten", "enable_biometric_auth_description": "Voer uw pincode in om biometrische authenticatie in te schakelen", "enabled": "Ingeschakeld", "end_date": "Einddatum", @@ -1485,6 +1489,7 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", + "queue_status": "Wachtrij {count}/{total}", "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", "rating_count": "{count, plural, one {# ster} other {# sterren}}", @@ -1915,6 +1920,7 @@ "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", "upload_concurrency": "Aantal gelijktijdige uploads", + "upload_details": "Uploaddetails", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", @@ -1965,6 +1971,7 @@ "view_album": "Bekijk album", "view_all": "Bekijk alle", "view_all_users": "Bekijk alle gebruikers", + "view_details": "Bekijk details", "view_in_timeline": "Bekijk in tijdlijn", "view_link": "Bekijk link", "view_links": "Links bekijken", From f16457d2f9acdb8161f3fee884d0e19f5c5f823e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:57:58 +0100 Subject: [PATCH 031/169] chore(deps): update redis:6.2-alpine docker digest to 7fe72c4 (#20065) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 26c3951278..ccb21e4587 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -35,7 +35,7 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0 + image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb database: image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea From 637eba6e089fc7ce7d37f61ab72d05ffa7f515aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:59:16 +0000 Subject: [PATCH 032/169] chore(deps): update node.js to v22.17.1 (#20066) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/.nvmrc | 2 +- cli/.nvmrc | 2 +- cli/package.json | 2 +- docs/.nvmrc | 2 +- docs/package.json | 2 +- e2e/.nvmrc | 2 +- e2e/package.json | 2 +- open-api/typescript-sdk/.nvmrc | 2 +- open-api/typescript-sdk/package.json | 2 +- server/.nvmrc | 2 +- server/package.json | 2 +- web/.nvmrc | 2 +- web/package.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/.nvmrc b/.github/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/cli/.nvmrc b/cli/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/cli/package.json b/cli/package.json index 3fde7e8b02..bd5b4b5757 100644 --- a/cli/package.json +++ b/cli/package.json @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/docs/.nvmrc b/docs/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/docs/package.json b/docs/package.json index b1faa4cfcb..97072d8eb5 100644 --- a/docs/package.json +++ b/docs/package.json @@ -59,6 +59,6 @@ "node": ">=20" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/e2e/.nvmrc b/e2e/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/e2e/package.json b/e2e/package.json index c780a93a24..1299b267f2 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -54,6 +54,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index c1bf8b4f65..677bc146cc 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/server/.nvmrc b/server/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/server/package.json b/server/package.json index 5a385193ec..91e68718bb 100644 --- a/server/package.json +++ b/server/package.json @@ -172,7 +172,7 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" }, "overrides": { "sharp": "^0.34.2" diff --git a/web/.nvmrc b/web/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/web/package.json b/web/package.json index 158c490b6a..2b092f057a 100644 --- a/web/package.json +++ b/web/package.json @@ -109,6 +109,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } From 5548033cae442d928ee02d03b20382c9d9610a69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:00:28 +0100 Subject: [PATCH 033/169] chore(deps): update dependency @types/multer to v2 (#20069) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 1239730fce..ca758d97e4 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -108,7 +108,7 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", + "@types/multer": "^2.0.0", "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", @@ -7239,9 +7239,9 @@ } }, "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/server/package.json b/server/package.json index 91e68718bb..86abe8979c 100644 --- a/server/package.json +++ b/server/package.json @@ -134,7 +134,7 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", + "@types/multer": "^2.0.0", "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", From 97daf42fd5e83206e2259292cb3a5d1e091bc975 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 10:24:00 -0500 Subject: [PATCH 034/169] chore: don't show beta switcher if connect to server below 1.136 (#20084) --- .../lib/widgets/settings/beta_timeline_list_tile.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index a9c873cb67..154ccb3552 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,11 +1,14 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -69,6 +72,13 @@ class _BetaTimelineListTileState extends ConsumerState final betaTimelineValue = ref .watch(appSettingsServiceProvider) .getSetting(AppSettingsEnum.betaTimeline); + final serverInfo = ref.watch(serverInfoProvider); + final auth = ref.watch(authProvider); + + if (!auth.isAuthenticated || + (serverInfo.serverVersion.minor < 136 && kReleaseMode)) { + return const SizedBox.shrink(); + } return AnimatedBuilder( animation: _animationController, From 2efca67217ea7c1306c02624c65dc321235aee82 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 11:24:32 -0500 Subject: [PATCH 035/169] feat(mobile): beta sync stats page (#19950) * show beta sync stats * show status next to jobs * use drift devtools reset database impl * dcm fixes * fix: hash count * styling --------- Co-authored-by: Alex --- i18n/en.json | 19 + mobile/lib/domain/services/asset.service.dart | 11 + .../domain/services/local_album.service.dart | 4 + .../lib/domain/services/memory.service.dart | 4 + .../domain/services/remote_album.service.dart | 4 + mobile/lib/domain/utils/background_sync.dart | 27 ++ .../repositories/local_album.repository.dart | 4 + .../repositories/local_asset.repository.dart | 10 + .../repositories/memory.repository.dart | 4 + .../repositories/remote_album.repository.dart | 4 + .../repositories/remote_asset.repository.dart | 4 + mobile/lib/pages/common/settings.page.dart | 55 +-- .../settings/beta_sync_settings.page.dart | 29 ++ .../providers/background_sync.provider.dart | 6 + .../lib/providers/sync_status.provider.dart | 69 +++- mobile/lib/routing/router.dart | 6 +- mobile/lib/routing/router.gr.dart | 16 + .../beta_sync_settings.dart | 348 ++++++++++++++++++ .../beta_sync_settings/entity_count_tile.dart | 99 +++++ .../lib/widgets/settings/settings_card.dart | 63 ++++ 20 files changed, 742 insertions(+), 44 deletions(-) create mode 100644 mobile/lib/pages/settings/beta_sync_settings.page.dart create mode 100644 mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart create mode 100644 mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart create mode 100644 mobile/lib/widgets/settings/settings_card.dart diff --git a/i18n/en.json b/i18n/en.json index 8961943678..ddcb01bf94 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Backward", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Manage the new sync system", "biometric_auth_enabled": "Biometric authentication enabled", "biometric_locked_out": "You are locked out of biometric authentication", "biometric_no_options": "No biometric options available", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Enable haptic feedback", "haptic_feedback_title": "Haptic Feedback", "has_quota": "Has quota", + "hash_asset": "Hash asset", + "hashed_assets": "Hashed assets", + "hashing": "Hashing", "header_settings_add_header_tip": "Add Header", "header_settings_field_validator_msg": "Value cannot be empty", "header_settings_header_name_input": "Header name", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Hour", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Image", @@ -1165,7 +1171,9 @@ "list": "List", "loading": "Loading", "loading_search_results_failed": "Loading search results failed", + "local": "Local", "local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server", + "local_assets": "Local Assets", "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", @@ -1359,6 +1367,7 @@ "original": "original", "other": "Other", "other_devices": "Other devices", + "other_entities": "Other entities", "other_variables": "Other variables", "owned": "Owned", "owner": "Owner", @@ -1519,6 +1528,8 @@ "refreshing_faces": "Refreshing faces", "refreshing_metadata": "Refreshing metadata", "regenerating_thumbnails": "Regenerating thumbnails", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Remove", "remove_assets_album_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from the album?", "remove_assets_shared_link_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from this shared link?", @@ -1556,6 +1567,9 @@ "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Are you sure you want to reset the SQLite database? You will need to log out and log in again to resync the data", + "reset_sqlite_success": "Successfully reset the SQLite database", "reset_to_default": "Reset to default", "resolve_duplicates": "Resolve duplicates", "resolved_all_duplicates": "Resolved all duplicates", @@ -1569,6 +1583,7 @@ "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", + "running": "Running", "save": "Save", "save_to_gallery": "Save to gallery", "saved_api_key": "Saved API Key", @@ -1822,6 +1837,7 @@ "storage_quota": "Storage Quota", "storage_usage": "{used} of {available} used", "submit": "Submit", + "success": "Success", "suggestions": "Suggestions", "sunrise_on_the_beach": "Sunrise on the beach", "support": "Support", @@ -1831,6 +1847,8 @@ "sync": "Sync", "sync_albums": "Sync albums", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", + "sync_local": "Sync Local", + "sync_remote": "Sync Remote", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Tag", "tag_assets": "Tag assets", @@ -1841,6 +1859,7 @@ "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", + "tap_to_run_job": "Tap to run job", "template": "Template", "theme": "Theme", "theme_selection": "Theme selection", diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 63b1aad8c1..995ed42dc8 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -76,4 +76,15 @@ class AssetService { Future> getPlaces() { return _remoteAssetRepository.getPlaces(); } + + Future<(int local, int remote)> getAssetCounts() async { + return ( + await _localAssetRepository.getCount(), + await _remoteAssetRepository.getCount() + ); + } + + Future getLocalHashedCount() { + return _localAssetRepository.getHashedCount(); + } } diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 79cc58f3e0..6c1479fdc9 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -18,4 +18,8 @@ class LocalAlbumService { Future update(LocalAlbum album) { return _repository.upsert(album); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart index c94b8a9f0a..50ed1e0f75 100644 --- a/mobile/lib/domain/services/memory.service.dart +++ b/mobile/lib/domain/services/memory.service.dart @@ -12,4 +12,8 @@ class DriftMemoryService { Future> getMemoryLane(String ownerId) { return _repository.getAll(ownerId); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index ebb24d5fe5..0f36fac2b9 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -147,4 +147,8 @@ class RemoteAlbumService { return _repository.addUsers(albumId, userIds); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 4a44c4d8f2..af66dda7a9 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -12,6 +12,14 @@ class BackgroundSyncManager { final SyncCallback? onRemoteSyncComplete; final SyncErrorCallback? onRemoteSyncError; + final SyncCallback? onLocalSyncStart; + final SyncCallback? onLocalSyncComplete; + final SyncErrorCallback? onLocalSyncError; + + final SyncCallback? onHashingStart; + final SyncCallback? onHashingComplete; + final SyncErrorCallback? onHashingError; + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; @@ -21,6 +29,12 @@ class BackgroundSyncManager { this.onRemoteSyncStart, this.onRemoteSyncComplete, this.onRemoteSyncError, + this.onLocalSyncStart, + this.onLocalSyncComplete, + this.onLocalSyncError, + this.onHashingStart, + this.onHashingComplete, + this.onHashingError, }); Future cancel() { @@ -47,6 +61,8 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.future; } + onLocalSyncStart?.call(); + // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full @@ -61,6 +77,10 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.whenComplete(() { _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }).catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; }); } @@ -70,10 +90,17 @@ class BackgroundSyncManager { return _hashTask!.future; } + onHashingStart?.call(); + _hashTask = runInIsolateGentle( computation: (ref) => ref.read(hashServiceProvider).hashAssets(), ); + return _hashTask!.whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }).catchError((error) { + onHashingError?.call(error.toString()); _hashTask = null; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 5f192a20cf..16c363b839 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -398,4 +398,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return results.isNotEmpty ? results.first : null; } + + Future getCount() { + return _db.managers.localAlbumEntity.count(); + } } diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 8d21c858a2..bf1ec37615 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -63,4 +63,14 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { return query.map((row) => row.toDto()).getSingleOrNull(); } + + Future getCount() { + return _db.managers.localAssetEntity.count(); + } + + Future getHashedCount() { + return _db.managers.localAssetEntity + .filter((e) => e.checksum.isNull().not()) + .count(); + } } diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index ff5f75c2ac..8582290c61 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -58,6 +58,10 @@ class DriftMemoryRepository extends DriftDatabaseRepository { return memoriesMap.values.toList(); } + + Future getCount() { + return _db.managers.memoryEntity.count(); + } } extension on MemoryEntityData { diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index c3c4570559..ca8dc2cfd5 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -268,6 +268,10 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { return album; }).watchSingleOrNull(); } + + Future getCount() { + return _db.managers.remoteAlbumEntity.count(); + } } extension on RemoteAlbumEntityData { diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 52cfe2e7c2..865c35be54 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -238,4 +238,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); }); } + + Future getCount() { + return _db.managers.remoteAssetEntity.count(); + } } diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index e45001270c..b19ff87aa9 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -13,6 +13,8 @@ import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; +import 'package:immich_mobile/entities/store.entity.dart' as app_store; +import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { advanced( @@ -97,47 +99,11 @@ class _MobileLayout extends StatelessWidget { Widget build(BuildContext context) { final List settings = SettingSection.values .map( - (setting) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Card( - elevation: 0, - clipBehavior: Clip.antiAlias, - color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - margin: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - leading: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(16)), - color: context.isDarkTheme - ? Colors.black26 - : Colors.white.withAlpha(100), - ), - padding: const EdgeInsets.all(16.0), - child: Icon(setting.icon, color: context.primaryColor), - ), - title: Text( - setting.title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ).tr(), - subtitle: Text( - setting.subtitle, - style: context.textTheme.labelLarge, - ).tr(), - onTap: () => - context.pushRoute(SettingsSubRoute(section: setting)), - ), - ), + (setting) => SettingsCard( + title: setting.title.tr(), + subtitle: setting.subtitle.tr(), + icon: setting.icon, + settingRoute: SettingsSubRoute(section: setting), ), ) .toList(); @@ -146,6 +112,13 @@ class _MobileLayout extends StatelessWidget { padding: const EdgeInsets.only(top: 10.0, bottom: 56), children: [ const BetaTimelineListTile(), + if (app_store.Store.isBetaTimelineEnabled) + SettingsCard( + icon: Icons.sync_outlined, + title: 'beta_sync'.tr(), + subtitle: 'beta_sync_subtitle'.tr(), + settingRoute: const BetaSyncSettingsRoute(), + ), ...settings, ], ); diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/beta_sync_settings.page.dart new file mode 100644 index 0000000000..ba23ccf5eb --- /dev/null +++ b/mobile/lib/pages/settings/beta_sync_settings.page.dart @@ -0,0 +1,29 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; + +@RoutePage() +class BetaSyncSettingsPage extends StatelessWidget { + const BetaSyncSettingsPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("beta_sync").t(context: context), + leading: IconButton( + onPressed: () => context.maybePop(true), + splashRadius: 24, + icon: const Icon( + Icons.arrow_back_ios_rounded, + ), + ), + ), + body: const BetaSyncSettings(), + ); + } +} diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index dc9cc0d59f..e6e83b64df 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -8,6 +8,12 @@ final backgroundSyncProvider = Provider((ref) { onRemoteSyncStart: syncStatusNotifier.startRemoteSync, onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, onRemoteSyncError: syncStatusNotifier.errorRemoteSync, + onLocalSyncStart: syncStatusNotifier.startLocalSync, + onLocalSyncComplete: syncStatusNotifier.completeLocalSync, + onLocalSyncError: syncStatusNotifier.errorLocalSync, + onHashingStart: syncStatusNotifier.startHashJob, + onHashingComplete: syncStatusNotifier.completeHashJob, + onHashingError: syncStatusNotifier.errorHashJob, ); ref.onDispose(manager.cancel); return manager; diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index 18d851aa19..5640ad6c89 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -1,43 +1,71 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum SyncStatus { idle, syncing, success, - error, + error; + + localized() { + return switch (this) { + SyncStatus.idle => "idle".tr(), + SyncStatus.syncing => "running".tr(), + SyncStatus.success => "success".tr(), + SyncStatus.error => "error".tr() + }; + } } class SyncStatusState { final SyncStatus remoteSyncStatus; + final SyncStatus localSyncStatus; + final SyncStatus hashJobStatus; + final String? errorMessage; const SyncStatusState({ this.remoteSyncStatus = SyncStatus.idle, + this.localSyncStatus = SyncStatus.idle, + this.hashJobStatus = SyncStatus.idle, this.errorMessage, }); SyncStatusState copyWith({ SyncStatus? remoteSyncStatus, + SyncStatus? localSyncStatus, + SyncStatus? hashJobStatus, String? errorMessage, }) { return SyncStatusState( remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus, + localSyncStatus: localSyncStatus ?? this.localSyncStatus, + hashJobStatus: hashJobStatus ?? this.hashJobStatus, errorMessage: errorMessage ?? this.errorMessage, ); } bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; + bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing; + bool get isHashing => hashJobStatus == SyncStatus.syncing; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is SyncStatusState && other.remoteSyncStatus == remoteSyncStatus && + other.localSyncStatus == localSyncStatus && + other.hashJobStatus == hashJobStatus && other.errorMessage == errorMessage; } @override - int get hashCode => Object.hash(remoteSyncStatus, errorMessage); + int get hashCode => Object.hash( + remoteSyncStatus, + localSyncStatus, + hashJobStatus, + errorMessage, + ); } class SyncStatusNotifier extends Notifier { @@ -46,9 +74,15 @@ class SyncStatusNotifier extends Notifier { return const SyncStatusState( errorMessage: null, remoteSyncStatus: SyncStatus.idle, + localSyncStatus: SyncStatus.idle, + hashJobStatus: SyncStatus.idle, ); } + /// + /// Remote Sync + /// + void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { state = state.copyWith( remoteSyncStatus: status, @@ -60,6 +94,37 @@ class SyncStatusNotifier extends Notifier { void completeRemoteSync() => setRemoteSyncStatus(SyncStatus.success); void errorRemoteSync(String error) => setRemoteSyncStatus(SyncStatus.error, error); + + /// + /// Local Sync + /// + + void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith( + localSyncStatus: status, + errorMessage: status == SyncStatus.error ? errorMessage : null, + ); + } + + void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); + void completeLocalSync() => setLocalSyncStatus(SyncStatus.success); + void errorLocalSync(String error) => + setLocalSyncStatus(SyncStatus.error, error); + + /// + /// Hash Job + /// + + void setHashJobStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith( + hashJobStatus: status, + errorMessage: status == SyncStatus.error ? errorMessage : null, + ); + } + + void startHashJob() => setHashJobStatus(SyncStatus.syncing); + void completeHashJob() => setHashJobStatus(SyncStatus.success); + void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); } final syncStatusProvider = diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index ba31ccef2b..5b3a92d4e2 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -73,6 +73,7 @@ import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; +import 'package:immich_mobile/pages/settings/beta_sync_settings.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; @@ -481,7 +482,6 @@ class AppRouter extends RootStackRouter { page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], ), - AutoRoute( page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard], @@ -495,6 +495,10 @@ class AppRouter extends RootStackRouter { page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: BetaSyncSettingsRoute.page, + guards: [_authGuard, _duplicateGuard], + ), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 0e24f776d8..a59b15856f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -503,6 +503,22 @@ class BackupOptionsRoute extends PageRouteInfo { ); } +/// generated route for +/// [BetaSyncSettingsPage] +class BetaSyncSettingsRoute extends PageRouteInfo { + const BetaSyncSettingsRoute({List? children}) + : super(BetaSyncSettingsRoute.name, initialChildren: children); + + static const String name = 'BetaSyncSettingsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const BetaSyncSettingsPage(); + }, + ); +} + /// generated route for /// [ChangeExperiencePage] class ChangeExperienceRoute extends PageRouteInfo { diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart new file mode 100644 index 0000000000..3d37fb102b --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -0,0 +1,348 @@ +import 'package:drift/drift.dart' as drift_db; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; + +class BetaSyncSettings extends HookConsumerWidget { + const BetaSyncSettings({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + final localAlbumService = ref.watch(localAlbumServiceProvider); + final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); + final memoryService = ref.watch(driftMemoryServiceProvider); + + Future> loadCounts() async { + final assetCounts = assetService.getAssetCounts(); + final localAlbumCounts = localAlbumService.getCount(); + final remoteAlbumCounts = remoteAlbumService.getCount(); + final memoryCount = memoryService.getCount(); + final getLocalHashedCount = assetService.getLocalHashedCount(); + + return await Future.wait([ + assetCounts, + localAlbumCounts, + remoteAlbumCounts, + memoryCount, + getLocalHashedCount, + ]); + } + + Future resetDatabase() async { +// https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + final drift = ref.read(driftProvider); + final database = drift.attachedDatabase; + await database.exclusively(() async { + // https://stackoverflow.com/a/65743498/25690041 + await database.customStatement('PRAGMA writable_schema = 1;'); + await database.customStatement('DELETE FROM sqlite_master;'); + await database.customStatement('VACUUM;'); + await database.customStatement('PRAGMA writable_schema = 0;'); + await database.customStatement('PRAGMA integrity_check'); + + await database.customStatement('PRAGMA user_version = 0'); + await database.beforeOpen( + // ignore: invalid_use_of_internal_member + database.resolvedEngine.executor, + drift_db.OpeningDetails(null, database.schemaVersion), + ); + await database.customStatement( + 'PRAGMA user_version = ${database.schemaVersion}', + ); + + // Refresh all stream queries + database.notifyUpdates({ + for (final table in database.allTables) + drift_db.TableUpdate.onTable(table), + }); + }); + } + + return FutureBuilder>( + future: loadCounts(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + + final assetCounts = snapshot.data![0]! as (int, int); + final localAssetCount = assetCounts.$1; + final remoteAssetCount = assetCounts.$2; + + final localAlbumCount = snapshot.data![1]! as int; + final remoteAlbumCount = snapshot.data![2]! as int; + final memoryCount = snapshot.data![3]! as int; + final localHashedCount = snapshot.data![4]! as int; + + return Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: ListView( + children: [ + _SectionHeaderText(text: "assets".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAssetCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAssetCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "albums".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAlbumCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAlbumCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "other".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "memories".t(context: context), + count: memoryCount, + icon: Icons.calendar_today, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: localHashedCount, + icon: Icons.tag, + ), + ), + ], + ), + ), + const Divider( + height: 1, + indent: 16, + endIndent: 16, + ), + const SizedBox(height: 24), + _SectionHeaderText(text: "jobs".t(context: context)), + ListTile( + title: Text( + "sync_local".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + leading: const Icon(Icons.sync), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).localSyncStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).syncLocal(full: true); + }, + ), + ListTile( + title: Text( + "sync_remote".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + leading: const Icon(Icons.cloud_sync), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).remoteSyncStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).syncRemote(); + }, + ), + ListTile( + title: Text( + "hash_asset".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + leading: const Icon(Icons.tag), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).hashJobStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).hashAssets(); + }, + ), + const Divider( + height: 1, + indent: 16, + endIndent: 16, + ), + const SizedBox(height: 24), + _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "reset_sqlite".t(context: context), + style: TextStyle( + color: context.colorScheme.error, + fontWeight: FontWeight.w500, + ), + ), + leading: Icon( + Icons.settings_backup_restore_rounded, + color: context.colorScheme.error, + ), + onTap: () async { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text( + "reset_sqlite".t(context: context), + ), + content: Text( + "reset_sqlite_confirmation".t(context: context), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text("cancel".t(context: context)), + ), + TextButton( + onPressed: () async { + await resetDatabase(); + context.pop(); + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: Text( + "reset_sqlite_success".t(context: context), + ), + ), + ); + }, + child: Text( + "confirm".t(context: context), + style: TextStyle( + color: context.colorScheme.error, + ), + ), + ), + ], + ); + }, + ); + }, + ), + ], + ), + ); + }, + ); + } +} + +class _SyncStatusIcon extends StatelessWidget { + final SyncStatus status; + + const _SyncStatusIcon({ + required this.status, + }); + + @override + Widget build(BuildContext context) { + return switch (status) { + SyncStatus.idle => const Icon( + Icons.pause_circle_outline_rounded, + ), + SyncStatus.syncing => const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + SyncStatus.success => const Icon( + Icons.check_circle_outline, + color: Colors.green, + ), + SyncStatus.error => Icon( + Icons.error_outline, + color: context.colorScheme.error, + ), + }; + } +} + +class _SectionHeaderText extends StatelessWidget { + final String text; + + const _SectionHeaderText({ + required this.text, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + text.toUpperCase(), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart new file mode 100644 index 0000000000..1e140fbf2e --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; + +class EntitiyCountTile extends StatelessWidget { + final int count; + final String label; + final IconData icon; + + const EntitiyCountTile({ + super.key, + required this.count, + required this.label, + required this.icon, + }); + + String zeroPadding(int number, int targetWidth) { + final numStr = number.toString(); + return numStr.length < targetWidth + ? "0" * (targetWidth - numStr.length) + : ""; + } + + int calculateMaxDigits(double availableWidth) { + const double charWidth = 11.0; + return (availableWidth / charWidth).floor().clamp(1, 20); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all( + width: 0.5, + color: context.colorScheme.outline.withAlpha(25), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon and Label + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + icon, + color: context.primaryColor, + ), + const SizedBox(width: 8), + Text( + label, + style: TextStyle( + color: context.primaryColor, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], + ), + const SizedBox(height: 12), + // Number + LayoutBuilder( + builder: (context, constraints) { + final maxDigits = calculateMaxDigits(constraints.maxWidth); + return RichText( + text: TextSpan( + style: const TextStyle( + fontSize: 18, + fontFamily: 'OverpassMono', + fontWeight: FontWeight.w600, + ), + children: [ + TextSpan( + text: zeroPadding(count, maxDigits), + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary + .withAlpha(75), + ), + ), + TextSpan( + text: count.toString(), + style: TextStyle( + color: context.primaryColor, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart new file mode 100644 index 0000000000..257c0ce2d6 --- /dev/null +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -0,0 +1,63 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SettingsCard extends StatelessWidget { + const SettingsCard({ + super.key, + required this.icon, + required this.title, + required this.subtitle, + required this.settingRoute, + }); + + final IconData icon; + final String title; + final String subtitle; + final PageRouteInfo settingRoute; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Card( + elevation: 0, + clipBehavior: Clip.antiAlias, + color: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + leading: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(16)), + color: context.isDarkTheme + ? Colors.black26 + : Colors.white.withAlpha(100), + ), + padding: const EdgeInsets.all(16.0), + child: Icon(icon, color: context.primaryColor), + ), + title: Text( + title, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + subtitle: Text( + subtitle, + style: context.textTheme.labelLarge, + ), + onTap: () => context.pushRoute(settingRoute), + ), + ), + ); + } +} From aa344a398984457d55b82584e2cfe1fec6442d7a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 11:36:00 -0500 Subject: [PATCH 036/169] feat: delete actions (#20034) * chore: show delete local * pr feedback * restore and perm delete action --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 12 ++++ .../repositories/timeline.repository.dart | 42 ++++++++++--- .../presentation/pages/drift_trash.page.dart | 24 ++++++++ ...elete_permanent_action_button.widget.dart} | 3 +- .../delete_trash_action_button.widget.dart | 59 +++++++++++++++++++ .../restore_trash_action_button.widget.dart | 56 ++++++++++++++++++ .../asset_viewer/bottom_sheet.widget.dart | 2 +- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 5 +- .../locked_folder_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../trash_bottom_sheet.widget.dart | 32 ++++++++++ .../infrastructure/action.provider.dart | 21 ++++++- .../timeline/multiselect.provider.dart | 7 +++ .../repositories/asset_api.repository.dart | 13 +++- mobile/lib/services/action.service.dart | 18 +++++- 18 files changed, 285 insertions(+), 18 deletions(-) rename mobile/lib/presentation/widgets/action_buttons/{delete_action_button.widget.dart => delete_permanent_action_button.widget.dart} (94%) create mode 100644 mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart create mode 100644 mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart create mode 100644 mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart diff --git a/i18n/en.json b/i18n/en.json index ddcb01bf94..bbd6debd5e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1575,6 +1575,7 @@ "resolved_all_duplicates": "Resolved all duplicates", "restore": "Restore", "restore_all": "Restore all", + "restore_trash_action_prompt": "{count} restored from trash", "restore_user": "Restore user", "restored_asset": "Restored asset", "resume": "Resume", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 865c35be54..2c5006953f 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -171,6 +171,18 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future restoreTrash(List ids) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + const RemoteAssetEntityCompanion(deletedAt: Value(null)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future delete(List ids) { return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 0c3eee59af..7bbae9a80a 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -332,6 +332,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { _remoteQueryBuilder( filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), groupBy: groupBy, + joinLocal: true, ); TimelineQuery archived(String userId, GroupAssetsBy groupBy) => @@ -443,11 +444,16 @@ class DriftTimelineRepository extends DriftDatabaseRepository { TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, + bool joinLocal = false, }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), - assetSource: (offset, count) => - _getRemoteAssets(filter: filter, offset: offset, count: count), + assetSource: (offset, count) => _getRemoteAssets( + filter: filter, + offset: offset, + count: count, + joinLocal: joinLocal, + ), ); } @@ -480,13 +486,35 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required Expression Function($RemoteAssetEntityTable row) filter, required int offset, required int count, + bool joinLocal = false, }) { - final query = _db.remoteAssetEntity.select() - ..where(filter) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + if (joinLocal) { + final query = _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum + .equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); - return query.map((row) => row.toDto()).get(); + return query.map((row) { + final asset = row.readTable(_db.remoteAssetEntity).toDto(); + final localId = row.read(_db.localAssetEntity.id); + return asset.copyWith(localId: localId); + }).get(); + } else { + final query = _db.remoteAssetEntity.select() + ..where(filter) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } } } diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 9cd2fac760..108a7e1cc9 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -2,8 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() @@ -29,6 +31,7 @@ class DriftTrashPage extends StatelessWidget { ), ], child: Timeline( + showStorageIndicator: true, appBar: SliverAppBar( title: Text('trash'.t(context: context)), floating: true, @@ -37,6 +40,27 @@ class DriftTrashPage extends StatelessWidget { centerTitle: true, elevation: 0, ), + topSliverWidgetHeight: 24, + topSliverWidget: Consumer( + builder: (context, ref, child) { + final trashDays = ref.watch( + serverInfoProvider.select((v) => v.serverConfig.trashDays), + ); + + return SliverPadding( + padding: const EdgeInsets.all(16.0), + sliver: SliverToBoxAdapter( + child: SizedBox( + height: 24.0, + child: const Text( + "trash_page_info", + ).t(context: context, args: {"days": "$trashDays"}), + ), + ), + ); + }, + ), + bottomSheet: const TrashBottomBar(), ), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart similarity index 94% rename from mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart rename to mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart index d81f998a7b..9f88d640dd 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -18,7 +18,8 @@ class DeletePermanentActionButton extends ConsumerWidget { return; } - final result = await ref.read(actionProvider.notifier).delete(source); + final result = + await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'delete_action_prompt'.t( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart new file mode 100644 index 0000000000..3ec75c60c2 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DeleteTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const DeleteTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = + await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'restore_trash_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: Icon( + Icons.delete_forever, + color: Colors.red[400], + ), + label: Text( + "delete".t(context: context), + style: TextStyle( + fontSize: 14, + color: Colors.red[400], + fontWeight: FontWeight.bold, + ), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart new file mode 100644 index 0000000000..05ee469d25 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class RestoreTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const RestoreTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).restoreTrash(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'restore_trash_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: const Icon( + Icons.history_rounded, + ), + label: Text( + 'restore'.t(), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 89822fef91..0e426fde6d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 9ed35da4cd..deaaea0d39 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index a1e1255a9f..8199271bfe 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 373d264d82..d83b8e399d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -44,6 +44,9 @@ class GeneralBottomSheet extends ConsumerWidget { : const DeletePermanentActionButton( source: ActionSource.timeline, ), + if (multiselect.hasLocal || multiselect.hasMerged) ...[ + const DeleteLocalActionButton(source: ActionSource.timeline), + ], const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton( diff --git a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart index 7f82f750f7..a644e6a035 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index cfb6fe4f1a..2e6047f0ba 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart new file mode 100644 index 0000000000..9f8216c4ed --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart'; + +class TrashBottomBar extends ConsumerWidget { + const TrashBottomBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SafeArea( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + height: 64, + child: Container( + color: context.themeData.canvasColor, + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DeleteTrashActionButton(source: ActionSource.timeline), + RestoreTrashActionButton(source: ActionSource.timeline), + ], + ), + ), + ), + ), + ); + } +} diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index cb025ef941..419ba0f902 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -206,6 +206,7 @@ class ActionNotifier extends Notifier { Future trash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + try { await _service.trash(ids); return ActionResult(count: ids.length, success: true); @@ -219,10 +220,26 @@ class ActionNotifier extends Notifier { } } - Future delete(ActionSource source) async { + Future restoreTrash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { - await _service.delete(ids); + await _service.restoreTrash(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to restore trash assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future deleteRemoteAndLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.deleteRemoteAndLocal(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index b1a926545d..9e0f690e7d 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -23,15 +23,22 @@ class MultiSelectState { }); bool get isEnabled => selectedAssets.isNotEmpty; + + /// Cloud only bool get hasRemote => selectedAssets.any( (asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged, ); + bool get hasLocal => selectedAssets.any( (asset) => asset.storage == AssetState.local, ); + bool get hasMerged => selectedAssets.any( + (asset) => asset.storage == AssetState.merged, + ); + MultiSelectState copyWith({ Set? selectedAssets, Set? lockedSelectionAssets, diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 4c854973b1..6d08b4f0d9 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -13,6 +13,7 @@ final assetApiRepositoryProvider = Provider( ref.watch(apiServiceProvider).assetsApi, ref.watch(apiServiceProvider).searchApi, ref.watch(apiServiceProvider).stacksApi, + ref.watch(apiServiceProvider).trashApi, ), ); @@ -20,8 +21,14 @@ class AssetApiRepository extends ApiRepository { final AssetsApi _api; final SearchApi _searchApi; final StacksApi _stacksApi; + final TrashApi _trashApi; - AssetApiRepository(this._api, this._searchApi, this._stacksApi); + AssetApiRepository( + this._api, + this._searchApi, + this._stacksApi, + this._trashApi, + ); Future update(String id, {String? description}) async { final response = await checkNull( @@ -56,6 +63,10 @@ class AssetApiRepository extends ApiRepository { return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force)); } + Future restoreTrash(List ids) async { + await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); + } + Future updateVisibility( List ids, AssetVisibilityEnum visibility, diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 7b0d74e420..63bc053a41 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -127,9 +127,25 @@ class ActionService { await _remoteAssetRepository.trash(remoteIds); } - Future delete(List remoteIds) async { + Future restoreTrash(List ids) async { + await _assetApiRepository.restoreTrash(ids); + await _remoteAssetRepository.restoreTrash(ids); + } + + Future deleteRemoteAndLocal( + List remoteIds, + List localIds, + ) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } Future deleteLocal(List localIds) async { From ab61bcfcc8c3c94aa1941f0b24352558eb05e2b6 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 22 Jul 2025 22:28:34 +0530 Subject: [PATCH 037/169] fix: reduce asset_viewer reloads (#20083) * fix: reduce asset_viewer reloads * limit the result of watch asset to one * fix null reference in map thumbnail * bump hash file limit to 256 --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- mobile/lib/constants/constants.dart | 2 +- .../repositories/remote_asset.repository.dart | 3 +- .../archive_action_button.widget.dart | 6 ++++ .../delete_local_action_button.widget.dart | 6 ++++ ...delete_permanent_action_button.widget.dart | 6 ++++ ...e_to_lock_folder_action_button.widget.dart | 6 ++++ .../trash_action_button.widget.dart | 6 ++++ .../asset_viewer/asset_viewer.page.dart | 30 ++++++++++++++----- .../asset_viewer/asset_viewer.state.dart | 4 +++ mobile/lib/widgets/map/map_thumbnail.dart | 15 +++++----- 10 files changed, 67 insertions(+), 17 deletions(-) diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 206fbbb28f..b54a1e9ca2 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -10,7 +10,7 @@ const int kSyncEventBatchSize = 5000; const int kFetchLocalAssetsBatchSize = 40000; // Hash batch limits -const int kBatchHashFileLimit = 128; +const int kBatchHashFileLimit = 256; const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 2c5006953f..64c602a2c7 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -62,7 +62,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id, _db.localAssetEntity.id, _db.stackEntity.primaryAssetId, - ]); + ]) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index 86537816e3..061e15e58a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class ArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).archive(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'archive_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 90534ca68c..acd3738f7a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class DeleteLocalActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).deleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'delete_local_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart index 9f88d640dd..7840e8a41d 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -22,6 +24,10 @@ class DeletePermanentActionButton extends ConsumerWidget { await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'delete_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index 7546f07961..6b3e32a2dc 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -22,6 +24,10 @@ class MoveToLockFolderActionButton extends ConsumerWidget { await ref.read(actionProvider.notifier).moveToLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'move_to_lock_folder_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index 449b688550..2863c71d12 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class TrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).trash(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'trash_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 9356c2f43e..34339c8ba3 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -85,6 +85,7 @@ class _AssetViewerState extends ConsumerState { bool blockGestures = false; bool dragInProgress = false; bool shouldPopOnDrag = false; + bool assetReloadRequested = false; double? initialScale; double previousExtent = _kBottomSheetMinimumExtent; Offset dragDownPosition = Offset.zero; @@ -404,7 +405,12 @@ class _AssetViewerState extends ConsumerState { void _onEvent(Event event) { if (event is TimelineReloadEvent) { - _onTimelineReload(event); + _onTimelineReloadEvent(); + return; + } + + if (event is ViewerReloadAssetEvent) { + assetReloadRequested = true; return; } @@ -417,14 +423,22 @@ class _AssetViewerState extends ConsumerState { } } - void _onTimelineReload(_) { - setState(() { - totalAssets = ref.read(timelineServiceProvider).totalAssets; - if (totalAssets == 0) { - context.maybePop(); - return; - } + void _onTimelineReloadEvent() { + totalAssets = ref.read(timelineServiceProvider).totalAssets; + if (totalAssets == 0) { + context.maybePop(); + return; + } + if (assetReloadRequested) { + assetReloadRequested = false; + _onAssetReloadEvent(); + return; + } + } + + void _onAssetReloadEvent() { + setState(() { final index = pageController.page?.round() ?? 0; final newAsset = ref.read(timelineServiceProvider).getAsset(index); final currentAsset = ref.read(currentAssetNotifier); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 825b637e8d..e57a6ff7ca 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -7,6 +7,10 @@ class ViewerOpenBottomSheetEvent extends Event { const ViewerOpenBottomSheetEvent(); } +class ViewerReloadAssetEvent extends Event { + const ViewerReloadAssetEvent(); +} + class AssetViewerState { final int backgroundOpacity; final bool showingBottomSheet; diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 1e4b061be6..5be15f2d5f 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -113,13 +113,14 @@ class MapThumbnail extends HookConsumerWidget { ), ValueListenableBuilder( valueListenable: position, - builder: (_, value, __) => value != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) - : const SizedBox.shrink(), + builder: (_, value, __) => + value != null && assetMarkerRemoteId != null + ? PositionedAssetMarkerIcon( + size: height / 2, + point: value, + assetRemoteId: assetMarkerRemoteId!, + ) + : const SizedBox.shrink(), ), ], ), From ac44f6d1e0ad026c509e8bb71fddf4457defb6fd Mon Sep 17 00:00:00 2001 From: Daimolean <92239625+wuzihao051119@users.noreply.github.com> Date: Wed, 23 Jul 2025 01:17:52 +0800 Subject: [PATCH 038/169] feat(mobile): asset face sync (#20022) * feat(mobile): asset face sync * fix: lint --------- Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v4.json | 1 + .../lib/domain/models/asset_face.model.dart | 98 + mobile/lib/domain/models/person.model.dart | 7 - .../domain/services/sync_stream.service.dart | 4 + .../entities/asset_face.entity.dart | 34 + .../entities/asset_face.entity.drift.dart | 1013 +++ .../entities/person.entity.dart | 3 - .../entities/person.entity.drift.dart | 60 +- .../repositories/asset_face.repository.dart | 33 + .../repositories/db.repository.dart | 8 +- .../repositories/db.repository.drift.dart | 31 +- .../repositories/db.repository.steps.dart | 449 ++ .../repositories/person.repository.dart | 1 - .../repositories/sync_api.repository.dart | 3 + .../repositories/sync_stream.repository.dart | 58 +- .../pages/dev/feat_in_development.page.dart | 1 + .../pages/dev/media_stat.page.dart | 4 + .../infrastructure/asset_face.provider.dart | 7 + mobile/lib/repositories/auth.repository.dart | 3 +- .../services/sync_stream_service_test.dart | 4 + mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v4.dart | 5524 +++++++++++++++++ 22 files changed, 7270 insertions(+), 81 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v4.json create mode 100644 mobile/lib/domain/models/asset_face.model.dart create mode 100644 mobile/lib/infrastructure/entities/asset_face.entity.dart create mode 100644 mobile/lib/infrastructure/entities/asset_face.entity.drift.dart create mode 100644 mobile/lib/infrastructure/repositories/asset_face.repository.dart create mode 100644 mobile/lib/providers/infrastructure/asset_face.provider.dart create mode 100644 mobile/test/drift/main/generated/schema_v4.dart diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json new file mode 100644 index 0000000000..82ef30adae --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/asset_face.model.dart b/mobile/lib/domain/models/asset_face.model.dart new file mode 100644 index 0000000000..f432b923e3 --- /dev/null +++ b/mobile/lib/domain/models/asset_face.model.dart @@ -0,0 +1,98 @@ +// Model for an asset face stored in the server +class AssetFace { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + + const AssetFace({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + + AssetFace copyWith({ + String? id, + String? assetId, + String? personId, + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) { + return AssetFace( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + String toString() { + return '''AssetFace { + id: $id, + assetId: $assetId, + personId: ${personId ?? ""}, + imageWidth: $imageWidth, + imageHeight: $imageHeight, + boundingBoxX1: $boundingBoxX1, + boundingBoxY1: $boundingBoxY1, + boundingBoxX2: $boundingBoxX2, + boundingBoxY2: $boundingBoxY2, + sourceType: $sourceType, +}'''; + } + + @override + bool operator ==(covariant AssetFace other) { + if (identical(this, other)) return true; + + return other.id == id && + other.assetId == assetId && + other.personId == personId && + other.imageWidth == imageWidth && + other.imageHeight == imageHeight && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY2 == boundingBoxY2 && + other.sourceType == sourceType; + } + + @override + int get hashCode { + return id.hashCode ^ + assetId.hashCode ^ + personId.hashCode ^ + imageWidth.hashCode ^ + imageHeight.hashCode ^ + boundingBoxX1.hashCode ^ + boundingBoxY1.hashCode ^ + boundingBoxX2.hashCode ^ + boundingBoxY2.hashCode ^ + sourceType.hashCode; + } +} diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index d9eee9ae06..2a6b31cb17 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -103,7 +103,6 @@ class Person { final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; @@ -116,7 +115,6 @@ class Person { required this.ownerId, required this.name, this.faceAssetId, - required this.thumbnailPath, required this.isFavorite, required this.isHidden, required this.color, @@ -130,7 +128,6 @@ class Person { String? ownerId, String? name, String? faceAssetId, - String? thumbnailPath, bool? isFavorite, bool? isHidden, String? color, @@ -143,7 +140,6 @@ class Person { ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -160,7 +156,6 @@ class Person { ownerId: $ownerId, name: $name, faceAssetId: ${faceAssetId ?? ""}, - thumbnailPath: $thumbnailPath, isFavorite: $isFavorite, isHidden: $isHidden, color: ${color ?? ""}, @@ -178,7 +173,6 @@ class Person { other.ownerId == ownerId && other.name == name && other.faceAssetId == faceAssetId && - other.thumbnailPath == thumbnailPath && other.isFavorite == isFavorite && other.isHidden == isHidden && other.color == color && @@ -193,7 +187,6 @@ class Person { ownerId.hashCode ^ name.hashCode ^ faceAssetId.hashCode ^ - thumbnailPath.hashCode ^ isFavorite.hashCode ^ isHidden.hashCode ^ color.hashCode ^ diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 9a7d91ced9..ca8295fc8f 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -244,6 +244,10 @@ class SyncStreamService { return _syncStreamRepository.updatePeopleV1(data.cast()); case SyncEntityType.personDeleteV1: return _syncStreamRepository.deletePeopleV1(data.cast()); + case SyncEntityType.assetFaceV1: + return _syncStreamRepository.updateAssetFacesV1(data.cast()); + case SyncEntityType.assetFaceDeleteV1: + return _syncStreamRepository.deleteAssetFacesV1(data.cast()); default: _logger.warning("Unknown sync data type: $type"); } diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.dart b/mobile/lib/infrastructure/entities/asset_face.entity.dart new file mode 100644 index 0000000000..c54e4e1848 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.dart @@ -0,0 +1,34 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class AssetFaceEntity extends Table with DriftDefaultsMixin { + const AssetFaceEntity(); + + TextColumn get id => text()(); + + TextColumn get assetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get personId => text() + .nullable() + .references(PersonEntity, #id, onDelete: KeyAction.setNull)(); + + IntColumn get imageWidth => integer()(); + + IntColumn get imageHeight => integer()(); + + IntColumn get boundingBoxX1 => integer()(); + + IntColumn get boundingBoxY1 => integer()(); + + IntColumn get boundingBoxX2 => integer()(); + + IntColumn get boundingBoxY2 => integer()(); + + TextColumn get sourceType => text()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart new file mode 100644 index 0000000000..140af60de1 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -0,0 +1,1013 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:drift/internal/modular.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i5; + +typedef $$AssetFaceEntityTableCreateCompanionBuilder + = i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, +}); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder + = i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, +}); + +final class $$AssetFaceEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$AssetFaceEntityTable, i1.AssetFaceEntityData> { + $$AssetFaceEntityTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .assetId, + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .id)); + + i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { + final $_column = $_itemColumn('asset_id')!; + + final manager = i3 + .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('remote_asset_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .personId, + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .id)); + + i5.$$PersonEntityTableProcessedTableManager? get personId { + final $_column = $_itemColumn('person_id'); + if ($_column == null) return null; + final manager = i5 + .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('person_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_personIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$AssetFaceEntityTableFilterComposer + extends i0.Composer { + $$AssetFaceEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get imageWidth => $composableBuilder( + column: $table.imageWidth, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get sourceType => $composableBuilder( + column: $table.sourceType, builder: (column) => i0.ColumnFilters(column)); + + i3.$$RemoteAssetEntityTableFilterComposer get assetId { + final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableFilterComposer get personId { + final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableOrderingComposer + extends i0.Composer { + $$AssetFaceEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column)); + + i3.$$RemoteAssetEntityTableOrderingComposer get assetId { + final i3.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableOrderingComposer get personId { + final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableAnnotationComposer + extends i0.Composer { + $$AssetFaceEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get imageWidth => $composableBuilder( + column: $table.imageWidth, builder: (column) => column); + + i0.GeneratedColumn get imageHeight => $composableBuilder( + column: $table.imageHeight, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, builder: (column) => column); + + i0.GeneratedColumn get sourceType => $composableBuilder( + column: $table.sourceType, builder: (column) => column); + + i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { + final i3.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableAnnotationComposer get personId { + final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId})> { + $$AssetFaceEntityTableTableManager( + i0.GeneratedDatabase db, i1.$AssetFaceEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$AssetFaceEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => i1 + .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => + i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => + i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({assetId = false, personId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (assetId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: + i1.$$AssetFaceEntityTableReferences._assetIdTable(db), + referencedColumn: i1.$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) as T; + } + if (personId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: + i1.$$AssetFaceEntityTableReferences._personIdTable(db), + referencedColumn: i1.$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$AssetFaceEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId})>; + +class $AssetFaceEntityTable extends i2.AssetFaceEntity + with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $AssetFaceEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _assetIdMeta = + const i0.VerificationMeta('assetId'); + @override + late final i0.GeneratedColumn assetId = i0.GeneratedColumn( + 'asset_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _personIdMeta = + const i0.VerificationMeta('personId'); + @override + late final i0.GeneratedColumn personId = i0.GeneratedColumn( + 'person_id', aliasedName, true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); + static const i0.VerificationMeta _imageWidthMeta = + const i0.VerificationMeta('imageWidth'); + @override + late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( + 'image_width', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _imageHeightMeta = + const i0.VerificationMeta('imageHeight'); + @override + late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( + 'image_height', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxX1Meta = + const i0.VerificationMeta('boundingBoxX1'); + @override + late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( + 'bounding_box_x1', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxY1Meta = + const i0.VerificationMeta('boundingBoxY1'); + @override + late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( + 'bounding_box_y1', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxX2Meta = + const i0.VerificationMeta('boundingBoxX2'); + @override + late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( + 'bounding_box_x2', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxY2Meta = + const i0.VerificationMeta('boundingBoxY2'); + @override + late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( + 'bounding_box_y2', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _sourceTypeMeta = + const i0.VerificationMeta('sourceType'); + @override + late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( + 'source_type', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('asset_id')) { + context.handle(_assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + } else if (isInserting) { + context.missing(_assetIdMeta); + } + if (data.containsKey('person_id')) { + context.handle(_personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta)); + } + if (data.containsKey('image_width')) { + context.handle( + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown( + data['image_width']!, _imageWidthMeta)); + } else if (isInserting) { + context.missing(_imageWidthMeta); + } + if (data.containsKey('image_height')) { + context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, _imageHeightMeta)); + } else if (isInserting) { + context.missing(_imageHeightMeta); + } + if (data.containsKey('bounding_box_x1')) { + context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, _boundingBoxX1Meta)); + } else if (isInserting) { + context.missing(_boundingBoxX1Meta); + } + if (data.containsKey('bounding_box_y1')) { + context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, _boundingBoxY1Meta)); + } else if (isInserting) { + context.missing(_boundingBoxY1Meta); + } + if (data.containsKey('bounding_box_x2')) { + context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, _boundingBoxX2Meta)); + } else if (isInserting) { + context.missing(_boundingBoxX2Meta); + } + if (data.containsKey('bounding_box_y2')) { + context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, _boundingBoxY2Meta)); + } else if (isInserting) { + context.missing(_boundingBoxY2Meta); + } + if (data.containsKey('source_type')) { + context.handle( + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown( + data['source_type']!, _sourceTypeMeta)); + } else if (isInserting) { + context.missing(_sourceTypeMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.AssetFaceEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + assetId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + personId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}person_id']), + imageWidth: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}image_width'])!, + imageHeight: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}image_height'])!, + boundingBoxX1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + boundingBoxY1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + boundingBoxX2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + boundingBoxY2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, + sourceType: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}source_type'])!, + ); + } + + @override + $AssetFaceEntityTable createAlias(String alias) { + return $AssetFaceEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData( + {required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['asset_id'] = i0.Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = i0.Variable(personId); + } + map['image_width'] = i0.Variable(imageWidth); + map['image_height'] = i0.Variable(imageHeight); + map['bounding_box_x1'] = i0.Variable(boundingBoxX1); + map['bounding_box_y1'] = i0.Variable(boundingBoxY1); + map['bounding_box_x2'] = i0.Variable(boundingBoxX2); + map['bounding_box_y2'] = i0.Variable(boundingBoxY2); + map['source_type'] = i0.Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + i1.AssetFaceEntityData copyWith( + {String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType}) => + i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: + data.imageWidth.present ? data.imageWidth.value : this.imageWidth, + imageHeight: + data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: + data.sourceType.present ? data.sourceType.value : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value assetId; + final i0.Value personId; + final i0.Value imageWidth; + final i0.Value imageHeight; + final i0.Value boundingBoxX1; + final i0.Value boundingBoxY1; + final i0.Value boundingBoxX2; + final i0.Value boundingBoxY2; + final i0.Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const i0.Value.absent(), + this.assetId = const i0.Value.absent(), + this.personId = const i0.Value.absent(), + this.imageWidth = const i0.Value.absent(), + this.imageHeight = const i0.Value.absent(), + this.boundingBoxX1 = const i0.Value.absent(), + this.boundingBoxY1 = const i0.Value.absent(), + this.boundingBoxX2 = const i0.Value.absent(), + this.boundingBoxY2 = const i0.Value.absent(), + this.sourceType = const i0.Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? assetId, + i0.Expression? personId, + i0.Expression? imageWidth, + i0.Expression? imageHeight, + i0.Expression? boundingBoxX1, + i0.Expression? boundingBoxY1, + i0.Expression? boundingBoxX2, + i0.Expression? boundingBoxY2, + i0.Expression? sourceType, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + i1.AssetFaceEntityCompanion copyWith( + {i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType}) { + return i1.AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = i0.Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = i0.Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = i0.Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = i0.Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = i0.Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = i0.Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = i0.Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = i0.Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = i0.Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/person.entity.dart b/mobile/lib/infrastructure/entities/person.entity.dart index 68dd04cb5f..75543baca3 100644 --- a/mobile/lib/infrastructure/entities/person.entity.dart +++ b/mobile/lib/infrastructure/entities/person.entity.dart @@ -16,11 +16,8 @@ class PersonEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); - // TODO: foreign key refering to asset faces TextColumn get faceAssetId => text().nullable()(); - TextColumn get thumbnailPath => text()(); - BoolColumn get isFavorite => boolean()(); BoolColumn get isHidden => boolean()(); diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart index f0ced63f0e..70639adc2f 100644 --- a/mobile/lib/infrastructure/entities/person.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -17,7 +17,6 @@ typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion required String ownerId, required String name, i0.Value faceAssetId, - required String thumbnailPath, required bool isFavorite, required bool isHidden, i0.Value color, @@ -31,7 +30,6 @@ typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion i0.Value ownerId, i0.Value name, i0.Value faceAssetId, - i0.Value thumbnailPath, i0.Value isFavorite, i0.Value isHidden, i0.Value color, @@ -94,10 +92,6 @@ class $$PersonEntityTableFilterComposer column: $table.faceAssetId, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); @@ -160,10 +154,6 @@ class $$PersonEntityTableOrderingComposer column: $table.faceAssetId, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnOrderings(column)); @@ -225,9 +215,6 @@ class $$PersonEntityTableAnnotationComposer i0.GeneratedColumn get faceAssetId => $composableBuilder( column: $table.faceAssetId, builder: (column) => column); - i0.GeneratedColumn get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, builder: (column) => column); - i0.GeneratedColumn get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => column); @@ -293,7 +280,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< i0.Value ownerId = const i0.Value.absent(), i0.Value name = const i0.Value.absent(), i0.Value faceAssetId = const i0.Value.absent(), - i0.Value thumbnailPath = const i0.Value.absent(), i0.Value isFavorite = const i0.Value.absent(), i0.Value isHidden = const i0.Value.absent(), i0.Value color = const i0.Value.absent(), @@ -306,7 +292,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, @@ -319,7 +304,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< required String ownerId, required String name, i0.Value faceAssetId = const i0.Value.absent(), - required String thumbnailPath, required bool isFavorite, required bool isHidden, i0.Value color = const i0.Value.absent(), @@ -332,7 +316,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, @@ -443,12 +426,6 @@ class $PersonEntityTable extends i2.PersonEntity late final i0.GeneratedColumn faceAssetId = i0.GeneratedColumn('face_asset_id', aliasedName, true, type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbnailPathMeta = - const i0.VerificationMeta('thumbnailPath'); - @override - late final i0.GeneratedColumn thumbnailPath = - i0.GeneratedColumn('thumbnail_path', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta('isFavorite'); @override @@ -487,7 +464,6 @@ class $PersonEntityTable extends i2.PersonEntity ownerId, name, faceAssetId, - thumbnailPath, isFavorite, isHidden, color, @@ -535,14 +511,6 @@ class $PersonEntityTable extends i2.PersonEntity faceAssetId.isAcceptableOrUnknown( data['face_asset_id']!, _faceAssetIdMeta)); } - if (data.containsKey('thumbnail_path')) { - context.handle( - _thumbnailPathMeta, - thumbnailPath.isAcceptableOrUnknown( - data['thumbnail_path']!, _thumbnailPathMeta)); - } else if (isInserting) { - context.missing(_thumbnailPathMeta); - } if (data.containsKey('is_favorite')) { context.handle( _isFavoriteMeta, @@ -586,8 +554,6 @@ class $PersonEntityTable extends i2.PersonEntity .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, faceAssetId: attachedDatabase.typeMapping.read( i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, isFavorite: attachedDatabase.typeMapping .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, isHidden: attachedDatabase.typeMapping @@ -618,7 +584,6 @@ class PersonEntityData extends i0.DataClass final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; @@ -630,7 +595,6 @@ class PersonEntityData extends i0.DataClass required this.ownerId, required this.name, this.faceAssetId, - required this.thumbnailPath, required this.isFavorite, required this.isHidden, this.color, @@ -646,7 +610,6 @@ class PersonEntityData extends i0.DataClass if (!nullToAbsent || faceAssetId != null) { map['face_asset_id'] = i0.Variable(faceAssetId); } - map['thumbnail_path'] = i0.Variable(thumbnailPath); map['is_favorite'] = i0.Variable(isFavorite); map['is_hidden'] = i0.Variable(isHidden); if (!nullToAbsent || color != null) { @@ -668,7 +631,6 @@ class PersonEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), name: serializer.fromJson(json['name']), faceAssetId: serializer.fromJson(json['faceAssetId']), - thumbnailPath: serializer.fromJson(json['thumbnailPath']), isFavorite: serializer.fromJson(json['isFavorite']), isHidden: serializer.fromJson(json['isHidden']), color: serializer.fromJson(json['color']), @@ -685,7 +647,6 @@ class PersonEntityData extends i0.DataClass 'ownerId': serializer.toJson(ownerId), 'name': serializer.toJson(name), 'faceAssetId': serializer.toJson(faceAssetId), - 'thumbnailPath': serializer.toJson(thumbnailPath), 'isFavorite': serializer.toJson(isFavorite), 'isHidden': serializer.toJson(isHidden), 'color': serializer.toJson(color), @@ -700,7 +661,6 @@ class PersonEntityData extends i0.DataClass String? ownerId, String? name, i0.Value faceAssetId = const i0.Value.absent(), - String? thumbnailPath, bool? isFavorite, bool? isHidden, i0.Value color = const i0.Value.absent(), @@ -712,7 +672,6 @@ class PersonEntityData extends i0.DataClass ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color.present ? color.value : this.color, @@ -727,9 +686,6 @@ class PersonEntityData extends i0.DataClass name: data.name.present ? data.name.value : this.name, faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present - ? data.thumbnailPath.value - : this.thumbnailPath, isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, @@ -747,7 +703,6 @@ class PersonEntityData extends i0.DataClass ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') @@ -758,7 +713,7 @@ class PersonEntityData extends i0.DataClass @override int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + faceAssetId, isFavorite, isHidden, color, birthDate); @override bool operator ==(Object other) => identical(this, other) || @@ -769,7 +724,6 @@ class PersonEntityData extends i0.DataClass other.ownerId == this.ownerId && other.name == this.name && other.faceAssetId == this.faceAssetId && - other.thumbnailPath == this.thumbnailPath && other.isFavorite == this.isFavorite && other.isHidden == this.isHidden && other.color == this.color && @@ -783,7 +737,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { final i0.Value ownerId; final i0.Value name; final i0.Value faceAssetId; - final i0.Value thumbnailPath; final i0.Value isFavorite; final i0.Value isHidden; final i0.Value color; @@ -795,7 +748,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { this.ownerId = const i0.Value.absent(), this.name = const i0.Value.absent(), this.faceAssetId = const i0.Value.absent(), - this.thumbnailPath = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.isHidden = const i0.Value.absent(), this.color = const i0.Value.absent(), @@ -808,7 +760,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { required String ownerId, required String name, this.faceAssetId = const i0.Value.absent(), - required String thumbnailPath, required bool isFavorite, required bool isHidden, this.color = const i0.Value.absent(), @@ -816,7 +767,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { }) : id = i0.Value(id), ownerId = i0.Value(ownerId), name = i0.Value(name), - thumbnailPath = i0.Value(thumbnailPath), isFavorite = i0.Value(isFavorite), isHidden = i0.Value(isHidden); static i0.Insertable custom({ @@ -826,7 +776,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { i0.Expression? ownerId, i0.Expression? name, i0.Expression? faceAssetId, - i0.Expression? thumbnailPath, i0.Expression? isFavorite, i0.Expression? isHidden, i0.Expression? color, @@ -839,7 +788,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (ownerId != null) 'owner_id': ownerId, if (name != null) 'name': name, if (faceAssetId != null) 'face_asset_id': faceAssetId, - if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, if (isFavorite != null) 'is_favorite': isFavorite, if (isHidden != null) 'is_hidden': isHidden, if (color != null) 'color': color, @@ -854,7 +802,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { i0.Value? ownerId, i0.Value? name, i0.Value? faceAssetId, - i0.Value? thumbnailPath, i0.Value? isFavorite, i0.Value? isHidden, i0.Value? color, @@ -866,7 +813,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -895,9 +841,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (faceAssetId.present) { map['face_asset_id'] = i0.Variable(faceAssetId.value); } - if (thumbnailPath.present) { - map['thumbnail_path'] = i0.Variable(thumbnailPath.value); - } if (isFavorite.present) { map['is_favorite'] = i0.Variable(isFavorite.value); } @@ -922,7 +865,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') diff --git a/mobile/lib/infrastructure/repositories/asset_face.repository.dart b/mobile/lib/infrastructure/repositories/asset_face.repository.dart new file mode 100644 index 0000000000..a9ad753d84 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/asset_face.repository.dart @@ -0,0 +1,33 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset_face.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftAssetFaceRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftAssetFaceRepository(this._db) : super(_db); + + Future> getAll() { + return _db.assetFaceEntity + .select() + .map((assetFace) => assetFace.toDto()) + .get(); + } +} + +extension on AssetFaceEntityData { + AssetFace toDto() { + return AssetFace( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 7562cf6ff5..ced148f855 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -5,6 +5,7 @@ import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -56,6 +57,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { MemoryAssetEntity, StackEntity, PersonEntity, + AssetFaceEntity, ], include: { 'package:immich_mobile/infrastructure/entities/merged_asset.drift', @@ -72,7 +74,7 @@ class Drift extends $Drift implements IDatabaseRepository { ); @override - int get schemaVersion => 3; + int get schemaVersion => 4; @override MigrationStrategy get migration => MigrationStrategy( @@ -94,6 +96,10 @@ class Drift extends $Drift implements IDatabaseRepository { // Removed foreign key constraint on stack.primaryAssetId await m.alterTable(TableMigration(v3.stackEntity)); }, + from3To4: (m, v4) async { + await m.alterTable(TableMigration(v4.personEntity)); + await m.create(v4.assetFaceEntity); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 0f822e57eb..7b722dfff6 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -31,9 +31,11 @@ import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift. as i14; import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i15; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' as i16; -import 'package:drift/internal/modular.dart' as i17; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i17; +import 'package:drift/internal/modular.dart' as i18; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); @@ -64,8 +66,10 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14.$MemoryAssetEntityTable(this); late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); - i16.MergedAssetDrift get mergedAssetDrift => i17.ReadDatabaseContainer(this) - .accessor(i16.MergedAssetDrift.new); + late final i16.$AssetFaceEntityTable assetFaceEntity = + i16.$AssetFaceEntityTable(this); + i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer(this) + .accessor(i17.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -88,7 +92,8 @@ abstract class $Drift extends i0.GeneratedDatabase { remoteAlbumUserEntity, memoryEntity, memoryAssetEntity, - personEntity + personEntity, + assetFaceEntity ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => @@ -227,6 +232,20 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('person_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update), + ], + ), ], ); @override @@ -268,4 +287,6 @@ class $DriftManager { i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); i15.$$PersonEntityTableTableManager get personEntity => i15.$$PersonEntityTableTableManager(_db, _db.personEntity); + i16.$$AssetFaceEntityTableTableManager get assetFaceEntity => + i16.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index a0703c3714..d8c35707ed 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1266,9 +1266,451 @@ final class Schema3 extends i0.VersionedSchema { i1.GeneratedColumn _column_75(String aliasedName) => i1.GeneratedColumn('primary_asset_id', aliasedName, false, type: i1.DriftSqlType.string); + +final class Schema4 extends i0.VersionedSchema { + Schema4({required super.database}) : super(version: 4); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); + final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(user_id, "key")', + ], + columns: [ + _column_25, + _column_26, + _column_27, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(shared_by_id, shared_with_id)', + ], + columns: [ + _column_28, + _column_29, + _column_30, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id)', + ], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_36, + _column_60, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(album_id, user_id)', + ], + columns: [ + _column_60, + _column_25, + _column_61, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, memory_id)', + ], + columns: [ + _column_36, + _column_68, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null); +} + +class Shape14 extends i0.VersionedTable { + Shape14({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +class Shape15 extends i0.VersionedTable { + Shape15({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn('person_id', aliasedName, true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); +i1.GeneratedColumn _column_77(String aliasedName) => + i1.GeneratedColumn('image_width', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_78(String aliasedName) => + i1.GeneratedColumn('image_height', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_79(String aliasedName) => + i1.GeneratedColumn('bounding_box_x1', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_80(String aliasedName) => + i1.GeneratedColumn('bounding_box_y1', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_81(String aliasedName) => + i1.GeneratedColumn('bounding_box_x2', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_82(String aliasedName) => + i1.GeneratedColumn('bounding_box_y2', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_83(String aliasedName) => + i1.GeneratedColumn('source_type', aliasedName, false, + type: i1.DriftSqlType.string); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -1282,6 +1724,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from2To3(migrator, schema); return 3; + case 3: + final schema = Schema4(database: database); + final migrator = i1.Migrator(database, schema); + await from3To4(migrator, schema); + return 4; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -1291,9 +1738,11 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, + from3To4: from3To4, )); diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart index 859765d63b..fa336c5480 100644 --- a/mobile/lib/infrastructure/repositories/person.repository.dart +++ b/mobile/lib/infrastructure/repositories/person.repository.dart @@ -26,7 +26,6 @@ extension on PersonEntityData { ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 11d58663e0..e8be84effb 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -58,6 +58,7 @@ class SyncApiRepository { SyncRequestType.partnerStacksV1, SyncRequestType.userMetadataV1, SyncRequestType.peopleV1, + SyncRequestType.assetFacesV1, ], ).toJson(), ); @@ -176,6 +177,8 @@ const _kResponseMap = { SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson, SyncEntityType.personV1: SyncPersonV1.fromJson, SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson, + SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson, + SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson, }; class _SyncAckV1 { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index e141c387be..1cca903566 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; @@ -546,11 +547,62 @@ class SyncStreamRepository extends DriftDatabaseRepository { Iterable data, ) async { try { - await _db.personEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.personId)), - ); + await _db.batch((batch) { + for (final person in data) { + batch.deleteWhere( + _db.personEntity, + (row) => row.id.equals(person.personId), + ); + } + }); } catch (error, stack) { _logger.severe('Error: deletePeopleV1', error, stack); + rethrow; + } + } + + Future updateAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + final companion = AssetFaceEntityCompanion( + assetId: Value(assetFace.assetId), + personId: Value(assetFace.personId), + imageWidth: Value(assetFace.imageWidth), + imageHeight: Value(assetFace.imageHeight), + boundingBoxX1: Value(assetFace.boundingBoxX1), + boundingBoxY1: Value(assetFace.boundingBoxY1), + boundingBoxX2: Value(assetFace.boundingBoxX2), + boundingBoxY2: Value(assetFace.boundingBoxY2), + sourceType: Value(assetFace.sourceType), + ); + + batch.insert( + _db.assetFaceEntity, + companion.copyWith(id: Value(assetFace.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateAssetFacesV1', error, stack); + rethrow; + } + } + + Future deleteAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + batch.deleteWhere( + _db.assetFaceEntity, + (row) => row.id.equals(assetFace.assetFaceId), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAssetFacesV1', error, stack); + rethrow; } } } diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 7ee151f94d..2334fc5227 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -121,6 +121,7 @@ final _features = [ await db.memoryAssetEntity.deleteAll(); await db.stackEntity.deleteAll(); await db.personEntity.deleteAll(); + await db.assetFaceEntity.deleteAll(); }, ), _Feature( diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index acd7b219b3..d1803498b4 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -170,6 +170,10 @@ final _remoteStats = [ name: 'People', load: (db) => db.managers.personEntity.count(), ), + _Stat( + name: 'AssetFaces', + load: (db) => db.managers.assetFaceEntity.count(), + ), ]; @RoutePage() diff --git a/mobile/lib/providers/infrastructure/asset_face.provider.dart b/mobile/lib/providers/infrastructure/asset_face.provider.dart new file mode 100644 index 0000000000..386609ba94 --- /dev/null +++ b/mobile/lib/providers/infrastructure/asset_face.provider.dart @@ -0,0 +1,7 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/asset_face.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; + +final driftAssetFaceProvider = Provider( + (ref) => DriftAssetFaceRepository(ref.watch(driftProvider)), +); diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 5cf357d5a4..1c51cedf96 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -34,11 +34,12 @@ class AuthRepository extends DatabaseRepository { _drift.userMetadataEntity.deleteAll(), _drift.partnerEntity.deleteAll(), _drift.stackEntity.deleteAll(), - _drift.personEntity.deleteAll(), + _drift.assetFaceEntity.deleteAll(), ]); // Drift deletions - parent entities await Future.wait([ _drift.memoryEntity.deleteAll(), + _drift.personEntity.deleteAll(), _drift.remoteAlbumEntity.deleteAll(), _drift.remoteAssetEntity.deleteAll(), _drift.userEntity.deleteAll(), diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index deb19dfcf8..f9d9c4fbe4 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -109,6 +109,10 @@ void main() { .thenAnswer(successHandler); when(() => mockSyncStreamRepo.deletePeopleV1(any())) .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetFacesV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())) + .thenAnswer(successHandler); sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 209e70d788..22131b11bb 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -6,6 +6,7 @@ import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; +import 'schema_v4.dart' as v4; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -17,10 +18,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v2.DatabaseAtV2(db); case 3: return v3.DatabaseAtV3(db); + case 4: + return v4.DatabaseAtV4(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3]; + static const versions = const [1, 2, 3, 4]; } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart new file mode 100644 index 0000000000..d02e2ff9c4 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -0,0 +1,5524 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn email = GeneratedColumn( + 'email', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + isAdmin: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, + email: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData( + {required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith( + {String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes}) => + UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, + updatedAt, quotaSizeInBytes, quotaUsageInBytes); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith( + {Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes}) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn localDateTime = + GeneratedColumn('local_date_time', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), + thumbHash: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), + visibility: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, + stackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent()}) => + RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: + localDateTime.present ? localDateTime.value : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: + data.visibility.present ? data.visibility.value : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId}) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum']), + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation}) => + LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, + height, durationInSeconds, id, checksum, isFavorite, orientation); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation}) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + @override + List get $columns => + [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + backupSelection: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, + marker_: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData( + {required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith( + {String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent()}) => + LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_}) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn city = GeneratedColumn( + 'city', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn state = GeneratedColumn( + 'state', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn country = GeneratedColumn( + 'country', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn('date_time_original', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn make = GeneratedColumn( + 'make', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn model = GeneratedColumn( + 'model', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + city: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}city']), + state: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}state']), + country: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}country']), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + exposureTime: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), + fNumber: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}f_number']), + fileSize: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}file_size']), + focalLength: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), + latitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}latitude']), + longitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}longitude']), + iso: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}iso']), + make: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}make']), + model: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}model']), + lens: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}lens']), + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}orientation']), + timeZone: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), + rating: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}rating']), + projectionType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData( + {required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: + serializer.fromJson(json['dateTimeOriginal']), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith( + {String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent()}) => + RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: + exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: + projectionType.present ? projectionType.value : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: + data.description.present ? data.description.value : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: + data.focalLength.present ? data.focalLength.value : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith( + {Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType}) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\'')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))'), + defaultValue: const CustomExpression('1')); + late final GeneratedColumn order = GeneratedColumn( + 'order', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, + order: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData( + {required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith( + {String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order}) => + RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: + data.description.present ? data.description.value : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, + ownerId, thumbnailAssetId, isActivityEnabled, order); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order}) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn role = GeneratedColumn( + 'role', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + role: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData( + {required this.albumId, required this.userId, required this.role}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith( + {String? albumId, String? userId, int? role}) => + RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith( + {Value? albumId, Value? userId, Value? role}) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn data = GeneratedColumn( + 'data', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + data: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}data'])!, + isSaved: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, + memoryAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, + seenAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), + showAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), + hideAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent()}) => + MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, + type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt}) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + memoryId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith( + {Value? assetId, Value? memoryId}) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent()}) => + PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate}) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + personId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}person_id']), + imageWidth: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}image_width'])!, + imageHeight: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}image_height'])!, + boundingBoxX1: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + boundingBoxY1: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + boundingBoxX2: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + boundingBoxY2: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, + sourceType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}source_type'])!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData( + {required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith( + {String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType}) => + AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: + data.imageWidth.present ? data.imageWidth.value : this.imageWidth, + imageHeight: + data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: + data.sourceType.present ? data.sourceType.value : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith( + {Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType}) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV4 extends GeneratedDatabase { + DatabaseAtV4(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity + ]; + @override + int get schemaVersion => 4; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} From 250548dea67c0e21196a65a766e54069690d00c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:42:07 +0000 Subject: [PATCH 039/169] fix(deps): update typescript-projects (#19939) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard Co-authored-by: Daniel Dietzler --- cli/package-lock.json | 161 ++-- e2e/package-lock.json | 416 +++++----- server/package-lock.json | 16 +- server/package.json | 2 +- web/package-lock.json | 731 ++++++++++-------- web/package.json | 10 +- .../settings/setting-accordion-state.svelte | 3 +- .../side-bar/purchase-info.svelte | 3 +- .../timeline-manager/day-group.svelte.ts | 7 +- .../group-insertion-cache.svelte.ts | 9 +- .../internal/operations-support.svelte.ts | 11 +- .../timeline-manager/month-group.svelte.ts | 7 +- .../timeline-manager.svelte.ts | 12 +- web/src/lib/modals/ApiKeyModal.svelte | 3 +- web/src/lib/utils/timeline-util.ts | 5 +- 15 files changed, 783 insertions(+), 613 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 0d3db4cffa..a575b630cc 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -682,9 +682,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1365,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1389,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -1430,14 +1430,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -1452,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1470,9 +1470,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -1487,14 +1487,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1511,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -1525,16 +1526,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1580,16 +1581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1604,13 +1605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2305,9 +2306,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2315,9 +2316,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2504,6 +2505,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -4125,15 +4139,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4196,9 +4211,9 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -4329,9 +4344,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1884b22dd5..3ff7670739 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -181,9 +181,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -734,9 +734,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -837,9 +837,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -856,13 +856,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -879,13 +879,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -900,9 +900,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -917,9 +917,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -934,9 +934,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -951,9 +951,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -968,9 +968,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -985,9 +985,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -1002,9 +1002,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -1019,9 +1019,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -1036,9 +1036,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -1055,13 +1055,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -1078,13 +1078,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1101,13 +1124,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1124,13 +1147,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1147,13 +1170,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1170,13 +1193,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1184,7 +1207,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1194,9 +1217,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1214,9 +1237,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1234,9 +1257,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -1356,12 +1379,13 @@ } }, "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.1.tgz", + "integrity": "sha512-JQEuMANYRVHs7lm7KY9PCIjkgJk73h4m4J+g2mkw2Vo1ugPZ17UJVqEH8F+HeAdjKz5do1OaLe7ArDz+z308gw==", "dev": true, "license": "MIT", "dependencies": { + "debug": "^4.4.1", "http-errors": "^2.0.0", "koa-compose": "^4.1.0", "path-to-regexp": "^6.3.0" @@ -1510,13 +1534,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -2101,17 +2125,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2125,7 +2149,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -2141,16 +2165,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2166,14 +2190,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2188,14 +2212,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2206,9 +2230,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -2223,14 +2247,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2247,9 +2272,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -2261,16 +2286,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2316,16 +2341,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2340,13 +2365,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3439,9 +3464,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3449,9 +3474,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3638,6 +3663,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -5154,17 +5192,17 @@ } }, "node_modules/oidc-provider": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.2.0.tgz", - "integrity": "sha512-L0JL1ymI/hLKzDRqYzhKluNfRRQUR0++q5fTTziniKmJgNrJ6DnI5h5SP6w8Z0U/3wZrCndpVmbbu0VpKpY0CA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.3.0.tgz", + "integrity": "sha512-JVocwYM+Fs76nOCED2hMf3iMfrzhN4jISmCYVuFhBEnZiFk3QlODzQXkO1XS/Spw8VwRlKxwIl3otkiintFIjw==", "dev": true, "license": "MIT", "dependencies": { "@koa/cors": "^5.0.0", - "@koa/router": "^13.1.0", + "@koa/router": "^13.1.1", "debug": "^4.4.1", "eta": "^3.5.0", - "jose": "^6.0.11", + "jose": "^6.0.12", "jsesc": "^3.1.0", "koa": "^3.0.0", "nanoid": "^5.1.5", @@ -5177,9 +5215,9 @@ } }, "node_modules/oidc-provider/node_modules/jose": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", - "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", + "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==", "dev": true, "license": "MIT", "funding": { @@ -5488,13 +5526,13 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -5507,9 +5545,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5993,9 +6031,9 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6011,27 +6049,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/shebang-command": { @@ -6778,15 +6817,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package-lock.json b/server/package-lock.json index ca758d97e4..207776873e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -30,7 +30,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", @@ -6031,9 +6031,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.2.0.tgz", - "integrity": "sha512-y45D+oYDgvL1fuFnauwUk8MwT54l0hWwnUAzzP0bVuwhsmVJFelKOGGMCRch0pcgyINilVlAEk0Xjtcu0Su4cw==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.2.tgz", + "integrity": "sha512-nVbo0KtBdZbj19lvfFpe0ZhjKPh6LE229+NyQLuTDt6dfaLzNRpSu/rHP+jlvdWBAk93slsoGyWDRldbqklpaA==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", @@ -6054,7 +6054,7 @@ "@react-email/render": "1.1.3", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.1.0", + "@react-email/tailwind": "1.2.2", "@react-email/text": "0.1.5" }, "engines": { @@ -6227,9 +6227,9 @@ } }, "node_modules/@react-email/tailwind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.1.0.tgz", - "integrity": "sha512-m4sh5d1c8P9TPA6Ea8qHrboE5s9PmRQREIreYMn1l5ca0pCV/UBEY15e1RgoaseAzy2cy+gwI+nKhMwqUJsD1g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", + "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/server/package.json b/server/package.json index 86abe8979c..d461f1d767 100644 --- a/server/package.json +++ b/server/package.json @@ -55,7 +55,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", diff --git a/web/package-lock.json b/web/package-lock.json index c1147447de..1d4d3bdb34 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -51,9 +51,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -81,14 +81,14 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" } }, @@ -496,9 +496,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -529,9 +529,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -561,6 +561,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", @@ -667,6 +684,31 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", @@ -718,9 +760,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -2142,9 +2184,9 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.6.1.tgz", - "integrity": "sha512-8oj/cXc/M1soGQOkkkuEzeaiE/LTa3MJnoRwoRzG7GOPKHOfNRJDzsCcx3s1GqxQlcoHc4BJK3HoU1m0OV9UMA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.7.0.tgz", + "integrity": "sha512-nvWETsp2IsUoRlt2dFg8swJ8BXmEAZCmOTpIrJ+SGGf54V+rYQ01IbIOPNkmzlejz0Tr0PmslzwMunie8eKU7A==", "dev": true, "license": "MIT", "dependencies": { @@ -2155,15 +2197,15 @@ "zimmerframe": "^1.1.2" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^5.0.0 || ^6.0.0-next.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", "svelte": "^5.0.0", - "vite": ">= 5.0.0" + "vite": "^6.3.0 || >=7.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.4.tgz", - "integrity": "sha512-BXK9hTbP8AeQIfoz6+P3uoyVYStVHc5CIKqoTSF7hXm3Q5P9BwFMdEus4jsQuhaYmXGHzukcGlxe2QrsE8BJfQ==", + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.1.tgz", + "integrity": "sha512-8H+fxDEp7Xq6tLFdrGdS5fLu6ONDQQ9DgyjboXpChubuFdfH9QoFX09ypssBpyNkJNZFt9eW3yLmXIc9CesPCA==", "dev": true, "license": "MIT", "dependencies": { @@ -2193,9 +2235,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.0.0.tgz", - "integrity": "sha512-mma5GJ23pYiWpTNbN//g9XI3Hfob3aAlXPP42qRtvjgTAU6pfJyLyNPTdLjFuj+jfC9JslP4J3AkeiJNhjtLLA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.0.tgz", + "integrity": "sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==", "dev": true, "license": "MIT", "dependencies": { @@ -2919,17 +2961,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2943,20 +2985,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2971,14 +3013,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2989,9 +3031,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -3006,14 +3048,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3030,9 +3073,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -3044,16 +3087,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3073,16 +3116,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3097,13 +3140,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3151,16 +3194,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3176,14 +3219,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3198,14 +3241,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3216,9 +3259,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -3233,9 +3276,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -3247,16 +3290,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3276,13 +3319,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3320,14 +3363,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3342,14 +3385,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3360,9 +3403,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -3377,15 +3420,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3402,9 +3445,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -3416,16 +3459,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3471,16 +3514,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3495,13 +3538,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5031,9 +5074,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5041,9 +5084,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5123,6 +5166,80 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/eslint-p/node_modules/@eslint/js": { + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/eslint-p/node_modules/eslint": { + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.30.1", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, "node_modules/eslint-plugin-compat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.2.tgz", @@ -5160,9 +5277,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.10.1.tgz", - "integrity": "sha512-csCh2x0ge/DugXC7dCANh46Igi7bjMZEy6rHZCdS13AoGVJSu7a90Kru3I8oMYLGEemPRE1hQXadxvRPVMAAXQ==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.11.0.tgz", + "integrity": "sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==", "dev": true, "license": "MIT", "dependencies": { @@ -5175,7 +5292,7 @@ "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", - "svelte-eslint-parser": "^1.2.0" + "svelte-eslint-parser": "^1.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5285,31 +5402,19 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@types/json-schema": "^7.0.15" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/eslint/node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", @@ -9093,9 +9198,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", - "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.3.0.tgz", + "integrity": "sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==", "dev": true, "license": "MIT", "dependencies": { @@ -9608,15 +9713,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9631,14 +9737,14 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -9653,14 +9759,14 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9671,9 +9777,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -9688,9 +9794,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -9702,16 +9808,16 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -9731,16 +9837,16 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9755,13 +9861,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -9888,9 +9994,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -10001,9 +10107,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -10018,9 +10124,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -10035,9 +10141,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -10052,9 +10158,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -10069,9 +10175,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -10086,9 +10192,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -10103,9 +10209,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -10120,9 +10226,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -10137,9 +10243,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -10154,9 +10260,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -10171,9 +10277,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -10188,9 +10294,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -10205,9 +10311,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -10222,9 +10328,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -10239,9 +10345,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -10256,9 +10362,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -10273,9 +10379,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -10290,9 +10396,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -10307,9 +10413,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -10324,9 +10430,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -10341,9 +10447,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -10358,9 +10464,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -10375,9 +10481,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -10392,9 +10498,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10405,31 +10511,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/vitefu": { diff --git a/web/package.json b/web/package.json index 2b092f057a..753b0a15a6 100644 --- a/web/package.json +++ b/web/package.json @@ -68,9 +68,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -98,14 +98,14 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" }, "volta": { diff --git a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte index 6b3ae81685..b0deb64316 100644 --- a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte +++ b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte @@ -12,6 +12,7 @@ import { goto } from '$app/navigation'; import type { Snippet } from 'svelte'; import { handlePromiseError } from '$lib/utils'; + import { SvelteURLSearchParams } from 'svelte/reactivity'; const getParamValues = (param: string) => { return new Set((page.url.searchParams.get(param) || '').split(' ').filter((x) => x !== '')); @@ -26,7 +27,7 @@ let { queryParam, state = writable(getParamValues(queryParam)), children }: Props = $props(); setAccordionState(state); - const searchParams = new URLSearchParams(page.url.searchParams); + const searchParams = new SvelteURLSearchParams(page.url.searchParams); $effect(() => { if ($state.size > 0) { diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 5a984e94be..627292ea1b 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -17,6 +17,7 @@ import { mdiClose, mdiInformationOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; + import { SvelteDate } from 'svelte/reactivity'; let showMessage = $state(false); let hoverMessage = $state(false); @@ -37,7 +38,7 @@ }; const hideButton = async (always: boolean) => { - const hideBuyButtonUntil = new Date(); + const hideBuyButtonUntil = new SvelteDate(); if (always) { hideBuyButtonUntil.setFullYear(2124); // see ya in 100 years diff --git a/web/src/lib/managers/timeline-manager/day-group.svelte.ts b/web/src/lib/managers/timeline-manager/day-group.svelte.ts index 2a949499ec..9d5008bf83 100644 --- a/web/src/lib/managers/timeline-manager/day-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/day-group.svelte.ts @@ -4,6 +4,7 @@ import type { CommonLayoutOptions } from '$lib/utils/layout-utils'; import { getJustifiedLayoutFromAssets, getPosition } from '$lib/utils/layout-utils'; import { plainDateTimeCompare } from '$lib/utils/timeline-util'; +import { SvelteSet } from 'svelte/reactivity'; import type { MonthGroup } from './month-group.svelte'; import type { AssetOperation, Direction, MoveAsset, TimelineAsset } from './types'; import { ViewerAsset } from './viewer-asset.svelte'; @@ -109,13 +110,13 @@ export class DayGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } - const unprocessedIds = new Set(ids); - const processedIds = new Set(); + const unprocessedIds = new SvelteSet(ids); + const processedIds = new SvelteSet(); const moveAssets: MoveAsset[] = []; let changedGeometry = false; for (const assetId of unprocessedIds) { diff --git a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts index 66cca61d45..e511df9bf0 100644 --- a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts +++ b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts @@ -1,5 +1,6 @@ import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import type { DayGroup } from './day-group.svelte'; import type { MonthGroup } from './month-group.svelte'; import type { TimelineAsset } from './types'; @@ -9,8 +10,8 @@ export class GroupInsertionCache { [year: number]: { [month: number]: { [day: number]: DayGroup } }; } = {}; unprocessedAssets: TimelineAsset[] = []; - changedDayGroups = new Set(); - newDayGroups = new Set(); + changedDayGroups = new SvelteSet(); + newDayGroups = new SvelteSet(); getDayGroup({ year, month, day }: TimelinePlainDate): DayGroup | undefined { return this.#lookupCache[year]?.[month]?.[day]; @@ -31,7 +32,7 @@ export class GroupInsertionCache { } get updatedBuckets() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.changedDayGroups) { updated.add(group.monthGroup); } @@ -39,7 +40,7 @@ export class GroupInsertionCache { } get bucketsWithNewDayGroups() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.newDayGroups) { updated.add(group.monthGroup); } diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts index 82ec78499b..4419de2103 100644 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts @@ -1,6 +1,7 @@ import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import { GroupInsertionCache } from '../group-insertion-cache.svelte'; import { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; @@ -18,7 +19,7 @@ export function addAssetsToMonthGroups( } const addContext = new GroupInsertionCache(); - const updatedMonthGroups = new Set(); + const updatedMonthGroups = new SvelteSet(); const monthCount = timelineManager.months.length; for (const asset of assets) { let month = getMonthGroupByDate(timelineManager, asset.localDateTime); @@ -63,12 +64,12 @@ export function runAssetOperation( options: { order: AssetOrder }, ) { if (ids.size === 0) { - return { processedIds: new Set(), unprocessedIds: ids, changedGeometry: false }; + return { processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false }; } - const changedMonthGroups = new Set(); - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); + const changedMonthGroups = new SvelteSet(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); const combinedMoveAssets: { asset: TimelineAsset; date: TimelinePlainDate }[][] = []; for (const month of timelineManager.months) { if (idsToProcess.size > 0) { diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index bbcfe88caa..9f7112963a 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -17,6 +17,7 @@ import { import { t } from 'svelte-i18n'; import { get } from 'svelte/store'; +import { SvelteSet } from 'svelte/reactivity'; import { DayGroup } from './day-group.svelte'; import { GroupInsertionCache } from './group-insertion-cache.svelte'; import type { TimelineManager } from './timeline-manager.svelte'; @@ -115,15 +116,15 @@ export class MonthGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } const { dayGroups } = this; let combinedChangedGeometry = false; - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); const combinedMoveAssets: MoveAsset[][] = []; let index = dayGroups.length; while (index--) { diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index 8aacd0a90a..c66a55fa11 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -6,7 +6,7 @@ import { CancellableTask } from '$lib/utils/cancellable-task'; import { toTimelineAsset, type TimelinePlainDateTime, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; import { clamp, debounce, isEqual } from 'lodash-es'; -import { SvelteSet } from 'svelte/reactivity'; +import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; import { updateIntersectionMonthGroup } from '$lib/managers/timeline-manager/internal/intersection-support.svelte'; import { updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte'; @@ -293,7 +293,7 @@ export class TimelineManager { }); this.months = timebuckets.map((timeBucket) => { - const date = new Date(timeBucket.timeBucket); + const date = new SvelteDate(timeBucket.timeBucket); return new MonthGroup( this, { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1 }, @@ -456,14 +456,14 @@ export class TimelineManager { } updateAssetOperation(ids: string[], operation: AssetOperation) { - runAssetOperation(this, new Set(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); + runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); } updateAssets(assets: TimelineAsset[]) { - const lookup = new Map(assets.map((asset) => [asset.id, asset])); + const lookup = new SvelteMap(assets.map((asset) => [asset.id, asset])); const { unprocessedIds } = runAssetOperation( this, - new Set(lookup.keys()), + new SvelteSet(lookup.keys()), (asset) => { updateObject(asset, lookup.get(asset.id)); return { remove: false }; @@ -480,7 +480,7 @@ export class TimelineManager { removeAssets(ids: string[]) { const { unprocessedIds } = runAssetOperation( this, - new Set(ids), + new SvelteSet(ids), () => { return { remove: true }; }, diff --git a/web/src/lib/modals/ApiKeyModal.svelte b/web/src/lib/modals/ApiKeyModal.svelte index 15902c8e53..55896c631d 100644 --- a/web/src/lib/modals/ApiKeyModal.svelte +++ b/web/src/lib/modals/ApiKeyModal.svelte @@ -9,6 +9,7 @@ import { mdiKeyVariant } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; + import { SvelteMap } from 'svelte/reactivity'; interface Props { apiKey: { name: string; permissions: Permission[] }; @@ -23,7 +24,7 @@ let selectedItems: Permission[] = $state(apiKey.permissions); let selectAllItems = $derived(selectedItems.length === Object.keys(Permission).length - 1); - const permissions: Map = new Map(); + const permissions: Map = new SvelteMap(); permissions.set('activity', [ Permission.ActivityCreate, diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index 7646d0d6d4..dc237c2223 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -3,6 +3,7 @@ import { locale } from '$lib/stores/preferences.store'; import { getAssetRatio } from '$lib/utils/asset-utils'; import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; import { DateTime, type LocaleOptions } from 'luxon'; +import { SvelteSet } from 'svelte/reactivity'; import { get } from 'svelte/store'; // Move type definitions to the top @@ -216,8 +217,8 @@ export const plainDateTimeCompare = (ascending: boolean, a: TimelinePlainDateTim return aDateTime.millisecond - bDateTime.millisecond; }; -export function setDifference(setA: Set, setB: Set): Set { - const result = new Set(); +export function setDifference(setA: Set, setB: Set): SvelteSet { + const result = new SvelteSet(); for (const value of setA) { if (!setB.has(value)) { result.add(value); From b3061f1e4f47e5ad5b28b69fb1f47a35fa8f098b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:18:29 +0100 Subject: [PATCH 040/169] fix(deps): update typescript-projects (#20086) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Dietzler --- web/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 1d4d3bdb34..51f60a83c3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -100,7 +100,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -9174,9 +9174,9 @@ } }, "node_modules/svelte-check": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.2.2.tgz", - "integrity": "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.0.tgz", + "integrity": "sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==", "dev": true, "license": "MIT", "dependencies": { From 277e39ac982c25b5074f7142959aaf23ca6cd325 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 15:01:18 -0500 Subject: [PATCH 041/169] fix(mobile): sync icon rotation direction (#20088) spin the sync icon the right direction --- mobile/lib/widgets/common/immich_sliver_app_bar.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index c7ddeca6e0..09c84e0c20 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -364,7 +364,10 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> child: Opacity( opacity: isSyncing ? 1.0 : _dismissalAnimation.value, child: Transform.rotate( - angle: _rotationAnimation.value * 2 * 3.14159, + angle: _rotationAnimation.value * + 2 * + 3.14159 * + -1, // Rotate counter-clockwise child: Icon( Icons.sync, size: 24, From 3c7f0a2900f47dc5c35868ca2459c964550ef1f5 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 16:02:52 -0500 Subject: [PATCH 042/169] chore(mobile): use hides instead of changing the namespace (#20090) --- mobile/lib/pages/common/settings.page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index b19ff87aa9..439e5068a3 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -1,7 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; @@ -13,7 +13,7 @@ import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; -import 'package:immich_mobile/entities/store.entity.dart' as app_store; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { @@ -112,7 +112,7 @@ class _MobileLayout extends StatelessWidget { padding: const EdgeInsets.only(top: 10.0, bottom: 56), children: [ const BetaTimelineListTile(), - if (app_store.Store.isBetaTimelineEnabled) + if (Store.isBetaTimelineEnabled) SettingsCard( icon: Icons.sync_outlined, title: 'beta_sync'.tr(), From f1cac122ed44d9efa26eada3fe4a2c661b1d0823 Mon Sep 17 00:00:00 2001 From: Sebastian Di Luzio Date: Tue, 22 Jul 2025 23:29:36 +0200 Subject: [PATCH 043/169] fix: more inclusive language (#20092) --- docs/docs/features/mobile-app.mdx | 4 ++-- i18n/en.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/features/mobile-app.mdx b/docs/docs/features/mobile-app.mdx index 38222479b0..cd837741f1 100644 --- a/docs/docs/features/mobile-app.mdx +++ b/docs/docs/features/mobile-app.mdx @@ -88,9 +88,9 @@ It will only reflect files you add. ::: If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually. -To overcome this limitation, the files must be removed from the blacklist by +To overcome this limitation, the files must be removed from the ignore list by App settings -> Advanced -> Duplicate Assets -> Clear :::info -Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization. +Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the ignore list again at the end of the synchronization. ::: diff --git a/i18n/en.json b/i18n/en.json index bbd6debd5e..4dc72b4c42 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -592,7 +592,7 @@ "cache_settings_clear_cache_button": "Clear cache", "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", + "cache_settings_duplicated_assets_subtitle": "Photos and videos that are ignore listed by the app", "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})", "cache_settings_statistics_album": "Library thumbnails", "cache_settings_statistics_full": "Full images", From 1011cdb37699a53ee61d876067d3cfc435e2eafb Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 17:11:06 -0500 Subject: [PATCH 044/169] chore: handle requeue upload when target albums changed (#20089) * chore: handle requeue upload when target albums changed * chore: remove debug --- i18n/en.json | 1 + .../lib/pages/backup/drift_backup.page.dart | 16 +- .../drift_backup_album_selection.page.dart | 364 ++++++++++-------- .../backup/drift_upload_detail.page.dart | 6 +- .../backup/drift_backup.provider.dart | 32 +- 5 files changed, 237 insertions(+), 182 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 4dc72b4c42..54c7ca6f1b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1330,6 +1330,7 @@ "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", + "no_uploads_in_progress": "No uploads in progress", "not_in_any_album": "Not in any album", "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 1b9ec8ad07..7780413399 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -44,9 +44,6 @@ class _DriftBackupPageState extends ConsumerState { (album) => album.backupSelection == BackupSelection.selected, ) .toList(); - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); return Scaffold( appBar: AppBar( @@ -85,14 +82,13 @@ class _DriftBackupPageState extends ConsumerState { onStart: () async => await startBackup(), onStop: () async => await stopBackup(), ), - if (uploadItems.isNotEmpty) - TextButton.icon( - icon: const Icon(Icons.info_outline_rounded), - onPressed: () => context.pushRoute( - const DriftUploadDetailRoute(), - ), - label: Text("view_details".t(context: context)), + TextButton.icon( + icon: const Icon(Icons.info_outline_rounded), + onPressed: () => context.pushRoute( + const DriftUploadDetailRoute(), ), + label: Text("view_details".t(context: context)), + ), ], ], ), diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index fd39f0a579..18d3ee1156 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; @@ -28,6 +29,8 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState { String _searchQuery = ''; bool _isSearchMode = false; + int _initialTotalAssetCount = 0; + bool _hasPopped = false; late ValueNotifier _enableSyncUploadAlbum; late TextEditingController _searchController; late FocusNode _searchFocusNode; @@ -43,6 +46,9 @@ class _DriftBackupAlbumSelectionPageState .read(appSettingsServiceProvider) .getSetting(AppSettingsEnum.syncAlbums); ref.read(backupAlbumProvider.notifier).getAll(); + + _initialTotalAssetCount = + ref.read(driftBackupProvider.select((p) => p.totalCount)); } @override @@ -79,179 +85,207 @@ class _DriftBackupAlbumSelectionPageState } } - return Scaffold( - appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: _isSearchMode - ? SearchField( - hintText: 'search_albums'.t(context: context), - autofocus: true, - controller: _searchController, - focusNode: _searchFocusNode, - onChanged: (value) => - setState(() => _searchQuery = value.trim()), + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + if (didPop && !_hasPopped) { + _hasPopped = true; + + await ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentTotalAssetCount = + ref.read(driftBackupProvider.select((p) => p.totalCount)); + + if (currentTotalAssetCount != _initialTotalAssetCount) { + final isBackupEnabled = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableBackup); + + if (!isBackupEnabled) { + return; + } + final backupNotifier = ref.read(driftBackupProvider.notifier); + + backupNotifier.cancel().then((_) { + backupNotifier.backup(); + }); + } + } + }, + child: Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () async => await context.maybePop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + title: _isSearchMode + ? SearchField( + hintText: 'search_albums'.t(context: context), + autofocus: true, + controller: _searchController, + focusNode: _searchFocusNode, + onChanged: (value) => + setState(() => _searchQuery = value.trim()), + ) + : const Text( + "backup_album_selection_page_select_albums", + ).t(context: context), + actions: [ + if (!_isSearchMode) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() { + _isSearchMode = true; + _searchQuery = ''; + }), ) - : const Text( - "backup_album_selection_page_select_albums", - ).t(context: context), - actions: [ - if (!_isSearchMode) - IconButton( - icon: const Icon(Icons.search), - onPressed: () => setState(() { - _isSearchMode = true; - _searchQuery = ''; - }), - ) - else - IconButton( - icon: const Icon(Icons.close), - onPressed: () => setState(() { - _isSearchMode = false; - _searchQuery = ''; - _searchController.clear(); - }), - ), - ], - elevation: 0, - ), - body: CustomScrollView( - physics: const ClampingScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).t(context: context), - ), - // Selected Album Chips - - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - _SelectedAlbumNameChips( - selectedBackupAlbums: selectedBackupAlbums, - ), - _ExcludedAlbumNameChips( - excludedBackupAlbums: excludedBackupAlbums, - ), - ], - ), - ), - - SettingsSwitchListTile( - valueNotifier: _enableSyncUploadAlbum, - title: "sync_albums".t(context: context), - subtitle: - "sync_upload_album_setting_subtitle".t(context: context), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), - onChanged: handleSyncAlbumToggle, - ), - - ListTile( - title: Text( - "albums_on_device_count".t( - context: context, - args: {'count': albumCount.toString()}, + else + IconButton( + icon: const Icon(Icons.close), + onPressed: () => setState(() { + _isSearchMode = false; + _searchQuery = ''; + _searchController.clear(); + }), + ), + ], + elevation: 0, + ), + body: CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, ), - style: context.textTheme.titleSmall, - ), - subtitle: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( - "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, ).t(context: context), ), - trailing: IconButton( - splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), - onPressed: () { - // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(10)), - ), - elevation: 5, - title: Text( - 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), - ).t(context: context), - content: SingleChildScrollView( - child: ListBody( - children: [ - const Text( - 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), - ).t(context: context), - ], - ), - ), - ); - }, - ); - }, - ), - ), + // Selected Album Chips - if (Platform.isAndroid) - _SelectAllButton( - filteredAlbums: filteredAlbums, - selectedBackupAlbums: selectedBackupAlbums, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + _SelectedAlbumNameChips( + selectedBackupAlbums: selectedBackupAlbums, + ), + _ExcludedAlbumNameChips( + excludedBackupAlbums: excludedBackupAlbums, + ), + ], + ), ), - ], + + SettingsSwitchListTile( + valueNotifier: _enableSyncUploadAlbum, + title: "sync_albums".t(context: context), + subtitle: "sync_upload_album_setting_subtitle" + .t(context: context), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.primary, + ), + onChanged: handleSyncAlbumToggle, + ), + + ListTile( + title: Text( + "albums_on_device_count".t( + context: context, + args: {'count': albumCount.toString()}, + ), + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + ), + ).t(context: context), + ), + trailing: IconButton( + splashRadius: 16, + icon: Icon( + Icons.info, + size: 20, + color: context.primaryColor, + ), + onPressed: () { + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(10)), + ), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: context.primaryColor, + ), + ).t(context: context), + content: SingleChildScrollView( + child: ListBody( + children: [ + const Text( + 'backup_album_selection_page_assets_scatter', + style: TextStyle( + fontSize: 14, + ), + ).t(context: context), + ], + ), + ), + ); + }, + ); + }, + ), + ), + + if (Platform.isAndroid) + _SelectAllButton( + filteredAlbums: filteredAlbums, + selectedBackupAlbums: selectedBackupAlbums, + ), + ], + ), ), - ), - SliverLayoutBuilder( - builder: (context, constraints) { - if (constraints.crossAxisExtent > 600) { - return _AlbumSelectionGrid( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } else { - return _AlbumSelectionList( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } - }, - ), - ], + SliverLayoutBuilder( + builder: (context, constraints) { + if (constraints.crossAxisExtent > 600) { + return _AlbumSelectionGrid( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } else { + return _AlbumSelectionList( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } + }, + ), + ], + ), ), ); } diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 66803265e6..058bfa1aaf 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -39,7 +39,7 @@ class DriftUploadDetailPage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( - Icons.cloud_upload_outlined, + Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3), ), @@ -79,7 +79,9 @@ class DriftUploadDetailPage extends ConsumerWidget { return Card( elevation: 0, - color: context.colorScheme.surfaceContainer, + color: item.isFailed != null + ? context.colorScheme.errorContainer + : context.colorScheme.surfaceContainer, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all( Radius.circular(16), diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index c51c40775e..3a2c7fd9ce 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -41,6 +41,7 @@ class DriftUploadStatus { final double progress; final int fileSize; final String networkSpeedAsString; + final bool? isFailed; const DriftUploadStatus({ required this.taskId, @@ -48,6 +49,7 @@ class DriftUploadStatus { required this.progress, required this.fileSize, required this.networkSpeedAsString, + this.isFailed, }); DriftUploadStatus copyWith({ @@ -56,6 +58,7 @@ class DriftUploadStatus { double? progress, int? fileSize, String? networkSpeedAsString, + bool? isFailed, }) { return DriftUploadStatus( taskId: taskId ?? this.taskId, @@ -63,12 +66,13 @@ class DriftUploadStatus { progress: progress ?? this.progress, fileSize: fileSize ?? this.fileSize, networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, + isFailed: isFailed ?? this.isFailed, ); } @override String toString() { - return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString)'; + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed)'; } @override @@ -79,7 +83,8 @@ class DriftUploadStatus { other.filename == filename && other.progress == progress && other.fileSize == fileSize && - other.networkSpeedAsString == networkSpeedAsString; + other.networkSpeedAsString == networkSpeedAsString && + other.isFailed == isFailed; } @override @@ -88,7 +93,8 @@ class DriftUploadStatus { filename.hashCode ^ progress.hashCode ^ fileSize.hashCode ^ - networkSpeedAsString.hashCode; + networkSpeedAsString.hashCode ^ + isFailed.hashCode; } Map toMap() { @@ -98,6 +104,7 @@ class DriftUploadStatus { 'progress': progress, 'fileSize': fileSize, 'networkSpeedAsString': networkSpeedAsString, + 'isFailed': isFailed, }; } @@ -108,6 +115,7 @@ class DriftUploadStatus { progress: map['progress'] as double, fileSize: map['fileSize'] as int, networkSpeedAsString: map['networkSpeedAsString'] as String, + isFailed: map['isFailed'] != null ? map['isFailed'] as bool : null, ); } @@ -235,6 +243,8 @@ class ExpBackupNotifier extends StateNotifier { } void _handleTaskStatusUpdate(TaskStatusUpdate update) { + final taskId = update.task.taskId; + switch (update.status) { case TaskStatus.complete: if (update.task.group == kBackupGroup) { @@ -245,14 +255,26 @@ class ExpBackupNotifier extends StateNotifier { } // Remove the completed task from the upload items - final taskId = update.task.taskId; if (state.uploadItems.containsKey(taskId)) { - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 1000), () { _removeUploadItem(taskId); }); } case TaskStatus.failed: + final currentItem = state.uploadItems[taskId]; + if (currentItem == null) { + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: currentItem.copyWith( + isFailed: true, + ), + }, + ); break; case TaskStatus.canceled: From 1a70896113cc05a64287d75e97400f7b75f3b712 Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Tue, 22 Jul 2025 22:17:06 -0400 Subject: [PATCH 045/169] feat(web): Remove from Stack (#19703) * - add component - update server's StackCreateDto for merge parameter - Update stackRepo to only merge stacks when merge=true (default) - update web action handlers to show stack changes * - make open-api * lint & format * - Add proper icon to 'remove from stack' - change web unstack icon to image-off-outline * - cleanup * - format & lint * - make open-api: StackCreateDto merge optional * initial addition of new endpoint * remove stack endpoint * - fix up remove stack endpoint - open-api * - Undo stackCreate merge parameter * - open-api typescript * open-api dart * Tests: - add tests - update assetStub.imageFrom2015 to have required stack attributes to include it with tests * update event name * Fix event name in test * remove asset_update check * - merge stack.removeAsset params into one object - refactor asset existence check (no need for asset fetch) - fix tests * Don't return updated stack * Create specialized stack id & primary asset fetch for asset removal checks * Correct new permission names * make sql * - fix open-api * - cleanup --- mobile/openapi/README.md | 1 + mobile/openapi/lib/api/stacks_api.dart | 45 +++++++++++++++++ open-api/immich-openapi-specs.json | 44 +++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 9 ++++ server/src/controllers/stack.controller.ts | 9 +++- server/src/queries/stack.repository.sql | 10 ++++ server/src/repositories/stack.repository.ts | 10 ++++ server/src/services/stack.service.spec.ts | 49 +++++++++++++++++++ server/src/services/stack.service.ts | 19 +++++++ server/src/validation.ts | 40 +++++++++------ server/test/fixtures/asset.stub.ts | 5 +- .../components/asset-viewer/actions/action.ts | 3 +- .../actions/remove-asset-from-stack.svelte | 31 ++++++++++++ .../actions/unstack-action.svelte | 4 +- .../asset-viewer/asset-viewer-nav-bar.svelte | 4 ++ .../asset-viewer/asset-viewer.svelte | 7 +++ .../photos-page/actions/stack-action.svelte | 4 +- .../components/photos-page/asset-grid.svelte | 17 +++++++ web/src/lib/constants.ts | 1 + 19 files changed, 289 insertions(+), 23 deletions(-) create mode 100644 web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 3e7fa4c2f1..b20a0694c5 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -221,6 +221,7 @@ Class | Method | HTTP request | Description *StacksApi* | [**deleteStack**](doc//StacksApi.md#deletestack) | **DELETE** /stacks/{id} | *StacksApi* | [**deleteStacks**](doc//StacksApi.md#deletestacks) | **DELETE** /stacks | *StacksApi* | [**getStack**](doc//StacksApi.md#getstack) | **GET** /stacks/{id} | +*StacksApi* | [**removeAssetFromStack**](doc//StacksApi.md#removeassetfromstack) | **DELETE** /stacks/{id}/assets/{assetId} | *StacksApi* | [**searchStacks**](doc//StacksApi.md#searchstacks) | **GET** /stacks | *StacksApi* | [**updateStack**](doc//StacksApi.md#updatestack) | **PUT** /stacks/{id} | *SyncApi* | [**deleteSyncAck**](doc//SyncApi.md#deletesyncack) | **DELETE** /sync/ack | diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 84f23ec55d..6d6c4506be 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -190,6 +190,51 @@ class StacksApi { return null; } + /// Performs an HTTP 'DELETE /stacks/{id}/assets/{assetId}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStackWithHttpInfo(String assetId, String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/stacks/{id}/assets/{assetId}' + .replaceAll('{assetId}', assetId) + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStack(String assetId, String id,) async { + final response = await removeAssetFromStackWithHttpInfo(assetId, id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. /// Parameters: /// diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 4acd431203..cd61f3e004 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -6746,6 +6746,50 @@ ] } }, + "/stacks/{id}/assets/{assetId}": { + "delete": { + "operationId": "removeAssetFromStack", + "parameters": [ + { + "name": "assetId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Stacks" + ] + } + }, "/sync/ack": { "delete": { "operationId": "deleteSyncAck", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index d5f7fde52a..81d279407c 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -3373,6 +3373,15 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +export function removeAssetFromStack({ assetId, id }: { + assetId: string; + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/stacks/${encodeURIComponent(id)}/assets/${encodeURIComponent(assetId)}`, { + ...opts, + method: "DELETE" + })); +} export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/controllers/stack.controller.ts b/server/src/controllers/stack.controller.ts index 238753734c..5b153a163b 100644 --- a/server/src/controllers/stack.controller.ts +++ b/server/src/controllers/stack.controller.ts @@ -6,7 +6,7 @@ import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto } from import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { StackService } from 'src/services/stack.service'; -import { UUIDParamDto } from 'src/validation'; +import { UUIDAssetIDParamDto, UUIDParamDto } from 'src/validation'; @ApiTags('Stacks') @Controller('stacks') @@ -54,4 +54,11 @@ export class StackController { deleteStack(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } + + @Delete(':id/assets/:assetId') + @Authenticated({ permission: Permission.StackUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + removeAssetFromStack(@Auth() auth: AuthDto, @Param() dto: UUIDAssetIDParamDto): Promise { + return this.service.removeAsset(auth, dto); + } } diff --git a/server/src/queries/stack.repository.sql b/server/src/queries/stack.repository.sql index a256cdfc76..94a24f69e4 100644 --- a/server/src/queries/stack.repository.sql +++ b/server/src/queries/stack.repository.sql @@ -143,3 +143,13 @@ from "stack" where "id" = $1::uuid + +-- StackRepository.getForAssetRemoval +select + "stackId" as "id", + "stack"."primaryAssetId" +from + "asset" + left join "stack" on "stack"."id" = "asset"."stackId" +where + "asset"."id" = $1 diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index fe16c8b5eb..ace9468177 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -152,4 +152,14 @@ export class StackRepository { .where('id', '=', asUuid(id)) .executeTakeFirst(); } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) + getForAssetRemoval(assetId: string) { + return this.db + .selectFrom('asset') + .leftJoin('stack', 'stack.id', 'asset.stackId') + .select(['stackId as id', 'stack.primaryAssetId']) + .where('asset.id', '=', assetId) + .executeTakeFirst(); + } } diff --git a/server/src/services/stack.service.spec.ts b/server/src/services/stack.service.spec.ts index 5c7b505cd9..5517cf17f8 100644 --- a/server/src/services/stack.service.spec.ts +++ b/server/src/services/stack.service.spec.ts @@ -188,4 +188,53 @@ describe(StackService.name, () => { }); }); }); + + describe('removeAsset', () => { + it('should require stack.update permissions', async () => { + await expect(sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: 'asset-id' })).rejects.toBeInstanceOf( + BadRequestException, + ); + + expect(mocks.stack.getForAssetRemoval).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the asset is not in the stack', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: null, primaryAssetId: null }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.imageFrom2015.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the assetId is the primaryAssetId', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it("should update the asset to nullify it's stack-id", async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image1.id }); + + expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.image1.id, stackId: null }); + expect(mocks.event.emit).toHaveBeenCalledWith('StackUpdate', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); }); diff --git a/server/src/services/stack.service.ts b/server/src/services/stack.service.ts index 18600abd12..c84ec70fbf 100644 --- a/server/src/services/stack.service.ts +++ b/server/src/services/stack.service.ts @@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto'; import { Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; +import { UUIDAssetIDParamDto } from 'src/validation'; @Injectable() export class StackService extends BaseService { @@ -58,6 +59,24 @@ export class StackService extends BaseService { await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id }); } + async removeAsset(auth: AuthDto, dto: UUIDAssetIDParamDto): Promise { + const { id: stackId, assetId } = dto; + await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [stackId] }); + + const stack = await this.stackRepository.getForAssetRemoval(assetId); + + if (!stack?.id || stack.id !== stackId) { + throw new BadRequestException('Asset not in stack'); + } + + if (stack.primaryAssetId === assetId) { + throw new BadRequestException("Cannot remove stack's primary asset"); + } + + await this.assetRepository.update({ id: assetId, stackId: null }); + await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id }); + } + private async findOrFail(id: string) { const stack = await this.stackRepository.getById(id); if (!stack) { diff --git a/server/src/validation.ts b/server/src/validation.ts index 049b5432d6..3f7e1c6f3b 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -63,6 +63,22 @@ export class FileNotEmptyValidator extends FileValidator { } } +type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; +export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { + const { optional, each, nullable, ...apiPropertyOptions } = { + optional: false, + each: false, + nullable: false, + ...options, + }; + return applyDecorators( + IsUUID('4', { each }), + ApiProperty({ format: 'uuid', ...apiPropertyOptions }), + optional ? Optional({ nullable }) : IsNotEmpty(), + each ? IsArray() : IsString(), + ); +}; + export class UUIDParamDto { @IsNotEmpty() @IsUUID('4') @@ -70,6 +86,14 @@ export class UUIDParamDto { id!: string; } +export class UUIDAssetIDParamDto { + @ValidateUUID() + id!: string; + + @ValidateUUID() + assetId!: string; +} + type PinCodeOptions = { optional?: boolean } & OptionalOptions; export const PinCode = (options?: PinCodeOptions & ApiPropertyOptions) => { const { optional, nullable, emptyToNull, ...apiPropertyOptions } = { @@ -131,22 +155,6 @@ export const ValidateHexColor = () => { return applyDecorators(...decorators); }; -type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; -export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { - const { optional, each, nullable, ...apiPropertyOptions } = { - optional: false, - each: false, - nullable: false, - ...options, - }; - return applyDecorators( - IsUUID('4', { each }), - ApiProperty({ format: 'uuid', ...apiPropertyOptions }), - optional ? Optional({ nullable }) : IsNotEmpty(), - each ? IsArray() : IsString(), - ); -}; - type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' }; export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => { const { optional, nullable, format, ...apiPropertyOptions } = { diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 991c5d2c4f..76cf71d34d 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -462,7 +462,7 @@ export const assetStub = { }), imageFrom2015: Object.freeze({ - id: 'asset-id-1', + id: 'asset-id-2015', status: AssetStatus.Active, deviceAssetId: 'device-asset-id', fileModifiedAt: new Date('2015-02-23T05:06:29.716Z'), @@ -484,6 +484,9 @@ export const assetStub = { duration: null, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.ext', faces: [], diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index d823f17df4..446a004cbb 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -1,6 +1,6 @@ import type { AssetAction } from '$lib/constants'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; -import type { AlbumResponseDto, StackResponseDto } from '@immich/sdk'; +import type { AlbumResponseDto, AssetResponseDto, StackResponseDto } from '@immich/sdk'; type ActionMap = { [AssetAction.ARCHIVE]: { asset: TimelineAsset }; @@ -15,6 +15,7 @@ type ActionMap = { [AssetAction.UNSTACK]: { assets: TimelineAsset[] }; [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset }; [AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto }; + [AssetAction.REMOVE_ASSET_FROM_STACK]: { stack: StackResponseDto | null; asset: AssetResponseDto }; [AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset }; [AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset }; }; diff --git a/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte b/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte new file mode 100644 index 0000000000..0c77f3a1a6 --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte @@ -0,0 +1,31 @@ + + + diff --git a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte index 1adeead05f..0c8192a9e3 100644 --- a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte @@ -4,7 +4,7 @@ import { deleteStack } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; import type { StackResponseDto } from '@immich/sdk'; - import { mdiImageMinusOutline } from '@mdi/js'; + import { mdiImageOffOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import type { OnAction } from './action'; @@ -23,4 +23,4 @@ }; - + diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index a376c37139..66061ebb01 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -9,6 +9,7 @@ import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte'; import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte'; import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte'; + import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte'; import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte'; import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte'; import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte'; @@ -195,6 +196,9 @@ {#if stack?.primaryAssetId !== asset.id} + {#if stack?.assets?.length > 2} + + {/if} {/if} {/if} {#if album} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 4f793e7b52..d82b2e6532 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -328,6 +328,13 @@ await handleGetAllAlbums(); break; } + case AssetAction.REMOVE_ASSET_FROM_STACK: { + stack = action.stack; + if (stack) { + asset = stack.assets[0]; + } + break; + } case AssetAction.SET_STACK_PRIMARY_ASSET: { stack = action.stack; break; diff --git a/web/src/lib/components/photos-page/actions/stack-action.svelte b/web/src/lib/components/photos-page/actions/stack-action.svelte index 1e817d9e61..8ed3dea22c 100644 --- a/web/src/lib/components/photos-page/actions/stack-action.svelte +++ b/web/src/lib/components/photos-page/actions/stack-action.svelte @@ -4,7 +4,7 @@ import type { OnStack, OnUnstack } from '$lib/utils/actions'; import { deleteStack, stackAssets } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; - import { mdiImageMinusOutline, mdiImageMultipleOutline } from '@mdi/js'; + import { mdiImageMultipleOutline, mdiImageOffOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; interface Props { @@ -42,7 +42,7 @@ {#if unstack} - + {:else} {/if} diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 2d40857f99..182b24f3eb 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -523,6 +523,23 @@ updateUnstackedAssetInTimeline(timelineManager, action.assets); break; } + case AssetAction.REMOVE_ASSET_FROM_STACK: { + timelineManager.addAssets([toTimelineAsset(action.asset)]); + if (action.stack) { + //Have to unstack then restack assets in timeline in order to update the stack count in the timeline. + updateUnstackedAssetInTimeline( + timelineManager, + action.stack.assets.map((asset) => toTimelineAsset(asset)), + ); + updateStackedAssetInTimeline(timelineManager, { + stack: action.stack, + toDeleteIds: action.stack.assets + .filter((asset) => asset.id !== action.stack?.primaryAssetId) + .map((asset) => asset.id), + }); + } + break; + } case AssetAction.SET_STACK_PRIMARY_ASSET: { //Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible. updateUnstackedAssetInTimeline( diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 1a40f8522e..b354989e17 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -11,6 +11,7 @@ export enum AssetAction { UNSTACK = 'unstack', KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset', + REMOVE_ASSET_FROM_STACK = 'remove-asset-from-stack', SET_VISIBILITY_LOCKED = 'set-visibility-locked', SET_VISIBILITY_TIMELINE = 'set-visibility-timeline', } From c7853fbe9d59b97ac2480a2e4ad105960bac1a8a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 23:21:58 -0500 Subject: [PATCH 046/169] chore: refactor download group (#20099) --- mobile/lib/constants/constants.dart | 3 +++ mobile/lib/main.dart | 8 ++++---- mobile/lib/repositories/download.repository.dart | 16 ++++++++-------- mobile/lib/services/download.service.dart | 8 ++++---- mobile/lib/utils/download.dart | 3 --- 5 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 mobile/lib/utils/download.dart diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index b54a1e9ca2..b3d9d138c4 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -20,6 +20,9 @@ const String kSecuredPinCode = "secured_pin_code"; const String kManualUploadGroup = 'manual_upload_group'; const String kBackupGroup = 'backup_group'; const String kBackupLivePhotoGroup = 'backup_live_photo_group'; +const String kDownloadGroupImage = 'group_image'; +const String kDownloadGroupVideo = 'group_video'; +const String kDownloadGroupLivePhoto = 'group_livephoto'; // Timeline constants const int kTimelineNoneSegmentSize = 120; diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f036fd9bc3..9f39a89b33 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; @@ -29,7 +30,6 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/licenses.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -101,7 +101,7 @@ Future initApp() async { ); await FileDownloader().trackTasksInGroup( - downloadGroupLivePhoto, + kDownloadGroupLivePhoto, markDownloadedComplete: false, ); @@ -179,7 +179,7 @@ class ImmichAppState extends ConsumerState void _configureFileDownloaderNotifications() { FileDownloader().configureNotificationForGroup( - downloadGroupImage, + kDownloadGroupImage, running: TaskNotification( 'downloading_media'.tr(), '${'file_name'.tr()}: {filename}', @@ -192,7 +192,7 @@ class ImmichAppState extends ConsumerState ); FileDownloader().configureNotificationForGroup( - downloadGroupVideo, + kDownloadGroupVideo, running: TaskNotification( 'downloading_media'.tr(), '${'file_name'.tr()}: {filename}', diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index f1dae3c251..a1ad4ee3d9 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -4,10 +4,10 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); @@ -33,19 +33,19 @@ class DownloadRepository { DownloadRepository() { _downloader.registerCallbacks( - group: downloadGroupImage, + group: kDownloadGroupImage, taskStatusCallback: (update) => onImageDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupVideo, + group: kDownloadGroupVideo, taskStatusCallback: (update) => onVideoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, taskStatusCallback: (update) => onLivePhotoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); @@ -66,7 +66,7 @@ class DownloadRepository { Future> getLiveVideoTasks() { return _downloader.database.allRecordsWithStatus( TaskStatus.complete, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, ); } @@ -100,7 +100,7 @@ class DownloadRepository { headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: isVideo ? downloadGroupVideo : downloadGroupImage, + group: isVideo ? kDownloadGroupVideo : kDownloadGroupImage, ); continue; } @@ -113,7 +113,7 @@ class DownloadRepository { headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); @@ -126,7 +126,7 @@ class DownloadRepository { .toUpperCase() .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index 98f1765d04..fbd3f406fd 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -10,7 +11,6 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( @@ -173,7 +173,7 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metadata: LivePhotosMetadata( part: LivePhotosPart.image, id: asset.remoteId!, @@ -184,7 +184,7 @@ class DownloadService { asset.fileName .toUpperCase() .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metadata: LivePhotosMetadata( part: LivePhotosPart.video, id: asset.remoteId!, @@ -201,7 +201,7 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: asset.isImage ? downloadGroupImage : downloadGroupVideo, + group: asset.isImage ? kDownloadGroupImage : kDownloadGroupVideo, ), ]; } diff --git a/mobile/lib/utils/download.dart b/mobile/lib/utils/download.dart deleted file mode 100644 index c701f353a2..0000000000 --- a/mobile/lib/utils/download.dart +++ /dev/null @@ -1,3 +0,0 @@ -const downloadGroupImage = 'group_image'; -const downloadGroupVideo = 'group_video'; -const downloadGroupLivePhoto = 'group_livephoto'; From c91382625cf3939a35bc74d8da9e651a0031a17e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:26:28 +0100 Subject: [PATCH 047/169] fix(deps): update typescript-projects (#20103) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard --- e2e/package-lock.json | 6 +- server/package-lock.json | 447 +++++++++++++++++++-------------------- server/package.json | 4 +- 3 files changed, 223 insertions(+), 234 deletions(-) diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 3ff7670739..278d3a7f93 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -5192,9 +5192,9 @@ } }, "node_modules/oidc-provider": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.3.0.tgz", - "integrity": "sha512-JVocwYM+Fs76nOCED2hMf3iMfrzhN4jISmCYVuFhBEnZiFk3QlODzQXkO1XS/Spw8VwRlKxwIl3otkiintFIjw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.4.0.tgz", + "integrity": "sha512-1mEUejJq7cQV/b6cw2nitqOyIlOJTfQ6RNwGFcA7/Pp+vKIWBn8p48ylFtogP3Hbvrkf9s9W5HUeFe+v1KpcEQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/server/package-lock.json b/server/package-lock.json index 207776873e..c0c048c554 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -297,6 +297,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics-cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -369,6 +382,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -1159,9 +1185,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1209,9 +1235,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1245,19 +1271,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", @@ -2736,6 +2749,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2757,9 +2783,9 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.5.tgz", + "integrity": "sha512-DQpWdr3ShO0BHWkHl3I4W/jR6R3pDtxyBlmrpTuZF+PXxQyBXNvsUne0Wyo6QHPEDi+pAz9XchBFoKbqOhcdTg==", "license": "MIT", "dependencies": { "file-type": "21.0.0", @@ -2788,9 +2814,9 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.5.tgz", + "integrity": "sha512-Qr25MEY9t8VsMETy7eXQ0cNXqu0lzuFrrTr+f+1G57ABCtV5Pogm7n9bF71OU2bnkDD32Bi4hQLeFR90cku3Tw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2862,14 +2888,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz", + "integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==", "license": "MIT", "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "2.0.1", + "multer": "2.0.2", "path-to-regexp": "8.2.0", "tslib": "2.8.1" }, @@ -2882,71 +2908,10 @@ "@nestjs/core": "^11.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", - "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.5.tgz", + "integrity": "sha512-DY3zNY+BjbrYpV/t8HL8ptrusrWK8J0cfkfY1iZsfCd+0/1+j8IKno+QMLkerNQAZ7/Frh5tkaKHVwWk18TkMw==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -3063,6 +3028,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -3117,9 +3095,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.5.tgz", + "integrity": "sha512-ZYRYF750SefmuIo7ZqPlHDcin1OHh6My0OkOfGEFjrD9mJ0vMVIpwMTOOkpzCfCcpqUuxeHBuecpiIn+NLrQbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3145,9 +3123,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", - "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.5.tgz", + "integrity": "sha512-mAM11HwyS7aeSUbXdOqHbNCRoHwB0OOb+cmx5sgxvszhdG0Y6bwR60nKA4+EXL9xUEeWoxmbfLmSHlTSIJ9GKA==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -5838,9 +5816,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", - "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", + "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -6627,9 +6605,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.11.tgz", - "integrity": "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.0.tgz", + "integrity": "sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6645,16 +6623,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.11", - "@swc/core-darwin-x64": "1.12.11", - "@swc/core-linux-arm-gnueabihf": "1.12.11", - "@swc/core-linux-arm64-gnu": "1.12.11", - "@swc/core-linux-arm64-musl": "1.12.11", - "@swc/core-linux-x64-gnu": "1.12.11", - "@swc/core-linux-x64-musl": "1.12.11", - "@swc/core-win32-arm64-msvc": "1.12.11", - "@swc/core-win32-ia32-msvc": "1.12.11", - "@swc/core-win32-x64-msvc": "1.12.11" + "@swc/core-darwin-arm64": "1.13.0", + "@swc/core-darwin-x64": "1.13.0", + "@swc/core-linux-arm-gnueabihf": "1.13.0", + "@swc/core-linux-arm64-gnu": "1.13.0", + "@swc/core-linux-arm64-musl": "1.13.0", + "@swc/core-linux-x64-gnu": "1.13.0", + "@swc/core-linux-x64-musl": "1.13.0", + "@swc/core-win32-arm64-msvc": "1.13.0", + "@swc/core-win32-ia32-msvc": "1.13.0", + "@swc/core-win32-x64-msvc": "1.13.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6666,9 +6644,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.11.tgz", - "integrity": "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.0.tgz", + "integrity": "sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==", "cpu": [ "arm64" ], @@ -6683,9 +6661,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.11.tgz", - "integrity": "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.0.tgz", + "integrity": "sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==", "cpu": [ "x64" ], @@ -6700,9 +6678,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.11.tgz", - "integrity": "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.0.tgz", + "integrity": "sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==", "cpu": [ "arm" ], @@ -6717,9 +6695,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.11.tgz", - "integrity": "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.0.tgz", + "integrity": "sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==", "cpu": [ "arm64" ], @@ -6734,9 +6712,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.11.tgz", - "integrity": "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.0.tgz", + "integrity": "sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==", "cpu": [ "arm64" ], @@ -6751,9 +6729,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.11.tgz", - "integrity": "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.0.tgz", + "integrity": "sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==", "cpu": [ "x64" ], @@ -6768,9 +6746,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.11.tgz", - "integrity": "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.0.tgz", + "integrity": "sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==", "cpu": [ "x64" ], @@ -6785,9 +6763,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.11.tgz", - "integrity": "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.0.tgz", + "integrity": "sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==", "cpu": [ "arm64" ], @@ -6802,9 +6780,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.11.tgz", - "integrity": "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.0.tgz", + "integrity": "sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==", "cpu": [ "ia32" ], @@ -6819,9 +6797,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.11.tgz", - "integrity": "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.0.tgz", + "integrity": "sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==", "cpu": [ "x64" ], @@ -7500,17 +7478,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7524,7 +7502,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7540,16 +7518,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7543,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7587,14 +7565,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7605,9 +7583,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -7622,14 +7600,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7646,9 +7625,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -7660,16 +7639,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7715,16 +7694,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7739,13 +7718,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8905,9 +8884,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.2.tgz", - "integrity": "sha512-bq0PSxPCWeNlFBc5yjBs3eR+e6GxIEIeHY0xxq6WELzG65GPjL+A2ni1NS7NroKsur0C3UJdabw51IswiSTSYw==", + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.4.tgz", + "integrity": "sha512-5wSHd0oXs2jS6P+6tay/01Iz0cWRK8iYcscKtpS/GewEq0bJZwbkMZc77GJVOT9SP+UQuXA2y+pQTdCQJel7kQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9556,16 +9535,16 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -9588,6 +9567,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/compression/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10685,9 +10673,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10695,9 +10683,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -13823,9 +13811,9 @@ "license": "MIT" }, "node_modules/nest-commander": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.17.0.tgz", - "integrity": "sha512-1R9vppZT2j/9njKiG0zYTDLAyQOj14KdGWdNuhluveK8VXoQepXNb0t09dRNWy4KCWrI7wDZ2tQTEwb43JyHOw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.18.0.tgz", + "integrity": "sha512-NWtodOl2aStnApWp9oajCoJW71lqN0CCjf9ygOWxpXnG3o4nQ8ZO5CgrExfVw2+0CVC877hr0rFR7FSu2rypGg==", "license": "MIT", "dependencies": { "@fig/complete-commander": "^3.0.0", @@ -13926,9 +13914,9 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -14687,9 +14675,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -15397,9 +15385,9 @@ } }, "node_modules/react-email": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", - "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.3.tgz", + "integrity": "sha512-LUKyk9nNVFuTqAyp4yCEQFQjBe+s8nl3VauMWuOhBZ4VhGnimbrnv01U8yD2YwzaHKtytS0U659x5dc/0+xu+Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -18447,15 +18435,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package.json b/server/package.json index d461f1d767..5dbe83381a 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -113,7 +113,6 @@ "validator": "^13.12.0" }, "devDependencies": { - "canvas": "^3.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", @@ -146,6 +145,7 @@ "@types/ua-parser-js": "^0.7.36", "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", + "canvas": "^3.1.0", "eslint": "^9.14.0", "eslint-config-prettier": "^10.0.0", "eslint-plugin-prettier": "^5.1.3", From 05d26dc68390b3831162bf09539b7270dd36875a Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:53:49 +0530 Subject: [PATCH 048/169] fix: remove safe area from bottom bar (#20102) * remove safe area from bottom bar * fix: video not playing in search view * stop foreground / background back on migration --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../pages/common/change_experience.page.dart | 15 +++++++ .../asset_viewer/bottom_bar.widget.dart | 45 +++++++++---------- .../asset_viewer/video_viewer.widget.dart | 22 ++++++++- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index a8569b25a0..3cd545ea33 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -2,10 +2,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; @@ -43,6 +47,17 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } + // Cancel uploads + await Store.put(StoreKey.backgroundBackup, false); + ref.read(backupProvider.notifier).configureBackgroundBackup( + enabled: false, + onBatteryInfo: () {}, + onError: (_) {}, + ); + ref.read(backupProvider.notifier).setAutoBackup(false); + ref.read(backupProvider.notifier).cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); + // Start listening to new websocket events ref.read(websocketProvider.notifier).stopListenToOldEvents(); ref.read(websocketProvider.notifier).startListeningToBetaEvents(); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index d35a315f48..cb558804d2 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -50,31 +50,30 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short4, child: isSheetOpen ? const SizedBox.shrink() - : SafeArea( - child: Theme( - data: context.themeData.copyWith( - iconTheme: - const IconThemeData(size: 22, color: Colors.white), - textTheme: context.themeData.textTheme.copyWith( - labelLarge: - context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), + : Theme( + data: context.themeData.copyWith( + iconTheme: + const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: + context.themeData.textTheme.labelLarge?.copyWith( + color: Colors.white, ), ), - child: Container( - height: asset.isVideo ? 160 : 80, - color: Colors.black.withAlpha(125), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), - ], - ), + ), + child: Container( + height: context.padding.bottom + (asset.isVideo ? 160 : 80), + color: Colors.black.withAlpha(125), + padding: EdgeInsets.only(bottom: context.padding.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: actions, + ), + ], ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 17880da3e7..2accf465ed 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,6 +27,26 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +bool _isCurrentAsset( + BaseAsset asset, + BaseAsset? currentAsset, +) { + if (asset is RemoteAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.id == asset.id, + LocalAsset localAsset => localAsset.remoteId == asset.id, + _ => false, + }; + } else if (asset is LocalAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.localId == asset.id, + LocalAsset localAsset => localAsset.id == asset.id, + _ => false, + }; + } + return false; +} + class NativeVideoViewer extends HookConsumerWidget { final BaseAsset asset; final bool showControls; @@ -56,7 +76,7 @@ class NativeVideoViewer extends HookConsumerWidget { // If the swipe is completed, `isCurrent` will be true for video B after a delay. // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. final currentAsset = useState(ref.read(currentAssetNotifier)); - final isCurrent = currentAsset.value == asset; + final isCurrent = _isCurrentAsset(asset, currentAsset.value); // Used to show the placeholder during hero animations for remote videos to avoid a stutter final isVisible = useState(Platform.isIOS && asset.hasLocal); From 1d9cc4ca5f0dec7fe402f69d6a4016979fdf3beb Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 23 Jul 2025 15:38:29 +0200 Subject: [PATCH 049/169] chore(web): update translations (#20082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alberto Serluca Co-authored-by: DevServs Co-authored-by: Florian Ostertag Co-authored-by: Gilbert Co-authored-by: Indrek Haav Co-authored-by: Jozef Gaal Co-authored-by: KecskeTech Co-authored-by: Kuno Claes Co-authored-by: MSDNicrosoft Co-authored-by: Matjaž T Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Vegard Fladby Co-authored-by: adri1m64 Co-authored-by: waclaw66 --- i18n/cs.json | 23 +++++++- i18n/de.json | 21 ++++++++ i18n/et.json | 23 +++++++- i18n/fr.json | 28 ++++++++++ i18n/hu.json | 58 +++++++++++--------- i18n/id.json | 10 +++- i18n/it.json | 12 +++++ i18n/lv.json | 11 ++++ i18n/nb_NO.json | 23 +++++++- i18n/nl.json | 24 ++++++++- i18n/ru.json | 47 +++++++++++----- i18n/sk.json | 117 +++++++++++++++++++++++----------------- i18n/sl.json | 25 ++++++++- i18n/zh_SIMPLIFIED.json | 23 +++++++- 14 files changed, 350 insertions(+), 95 deletions(-) diff --git a/i18n/cs.json b/i18n/cs.json index c14a4b6d0c..80ea9674c0 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", + "beta_sync": "Stav synchronizace beta verze", + "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", "biometric_no_options": "Biometrické možnosti nejsou k dispozici", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", - "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace ignoruje", "cache_settings_duplicated_assets_title": "Duplicitní položky ({count})", "cache_settings_statistics_album": "Knihovna náhledů", "cache_settings_statistics_full": "Kompletní fotografie", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu", "haptic_feedback_title": "Dotyková zpětná vazba", "has_quota": "Má kvótu", + "hash_asset": "Hash položky", + "hashed_assets": "Hashované položky", + "hashing": "Hashování", "header_settings_add_header_tip": "Přidat hlavičku", "header_settings_field_validator_msg": "Hodnota nemůže být prázdná", "header_settings_header_name_input": "Název hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostitel", "hour": "Hodina", "id": "ID", + "idle": "Nečinnost", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server", "image": "Obrázek", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Načítání", "loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo", + "local": "Místní", "local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru", + "local_assets": "Místní položky", "local_network": "Místní síť", "local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "Oprávnění polohy", @@ -1322,6 +1330,7 @@ "no_results": "Žádné výsledky", "no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo", "no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti", + "no_uploads_in_progress": "Neprobíhá žádné nahrávání", "not_in_any_album": "Bez alba", "not_selected": "Není vybráno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatní", "other_devices": "Ostatní zařízení", + "other_entities": "Ostatní entity", "other_variables": "Další proměnné", "owned": "Vlastní", "owner": "Vlastník", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovování obličejů", "refreshing_metadata": "Obnovování metadat", "regenerating_thumbnails": "Regenerace miniatur", + "remote": "Vzdálený", + "remote_assets": "Vzdálené položky", "remove": "Odstranit", "remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", "remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", @@ -1556,11 +1568,15 @@ "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", "reset_pin_code": "Resetovat PIN kód", + "reset_sqlite": "Obnovit SQLite databázi", + "reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit SQLite databázi? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit", + "reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně", "reset_to_default": "Obnovit výchozí nastavení", "resolve_duplicates": "Vyřešit duplicity", "resolved_all_duplicates": "Vyřešeny všechny duplicity", "restore": "Obnovit", "restore_all": "Obnovit vše", + "restore_trash_action_prompt": "{count} obnoveno z koše", "restore_user": "Obnovit uživatele", "restored_asset": "Položka obnovena", "resume": "Pokračovat", @@ -1569,6 +1585,7 @@ "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Probíhá", "save": "Uložit", "save_to_gallery": "Uložit do galerie", "saved_api_key": "API klíč uložen", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvóta úložiště", "storage_usage": "Využito {used} z {available}", "submit": "Odeslat", + "success": "Úspěch", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slunce na pláži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Synchronizovat", "sync_albums": "Synchronizovat alba", "sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb", + "sync_local": "Synchronizovat místní", + "sync_remote": "Synchronizovat vzdálené", "sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich", "tag": "Značka", "tag_assets": "Přiřadit značku", @@ -1841,6 +1861,7 @@ "tag_updated": "Aktualizována značka: {tag}", "tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}", "tags": "Značky", + "tap_to_run_job": "Klepnutím na spustíte úlohu", "template": "Šablona", "theme": "Motiv", "theme_selection": "Výběr motivu", diff --git a/i18n/de.json b/i18n/de.json index 79ba1251d5..45245b182b 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", + "beta_sync": "Status des Beta Sync", + "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", "biometric_no_options": "Keine biometrischen Optionen verfügbar", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Haptisches Feedback aktivieren", "haptic_feedback_title": "Haptisches Feedback", "has_quota": "Kontingent", + "hash_asset": "Dateihash", + "hashed_assets": "Gehashte Dateien", + "hashing": "Hashen", "header_settings_add_header_tip": "Header hinzufügen", "header_settings_field_validator_msg": "Der Wert darf nicht leer sein", "header_settings_header_name_input": "Header-Name", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Stunde", "id": "ID", + "idle": "Untätig", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local": "Lokal", "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", + "local_assets": "Lokale Dateien", "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", @@ -1322,6 +1330,7 @@ "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", + "no_uploads_in_progress": "Kein Upload in Bearbeitung", "not_in_any_album": "In keinem Album", "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", @@ -1359,6 +1368,7 @@ "original": "Original", "other": "Sonstiges", "other_devices": "Andere Geräte", + "other_entities": "Andere Entitäten", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", + "remote": "Entfernt", + "remote_assets": "Entfernte Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1556,11 +1568,15 @@ "reset_password": "Passwort zurücksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen", "reset_pin_code": "PIN Code zurücksetzen", + "reset_sqlite": "SQLite Datenbank zurücksetzen", + "reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren", + "reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt", "reset_to_default": "Auf Standard zurücksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelöst", "restore": "Wiederherstellen", "restore_all": "Alle wiederherstellen", + "restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt", "restore_user": "Nutzer wiederherstellen", "restored_asset": "Datei wiederhergestellt", "resume": "Fortsetzen", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", + "running": "Läuft", "save": "Speichern", "save_to_gallery": "In Galerie speichern", "saved_api_key": "API-Schlüssel wurde gespeichert", @@ -1822,6 +1839,7 @@ "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", + "success": "Erfolgreich", "suggestions": "Vorschläge", "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "Unterstützung", @@ -1831,6 +1849,8 @@ "sync": "Synchronisieren", "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", + "sync_local": "Lokal synchronisieren", + "sync_remote": "Entfernt synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1841,6 +1861,7 @@ "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", "tags": "Tags", + "tap_to_run_job": "Tippen um den Job zu starten", "template": "Vorlage", "theme": "Theme", "theme_selection": "Themenauswahl", diff --git a/i18n/et.json b/i18n/et.json index 804d0c6c46..1d293a3bdf 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Varundamise valikud", "backup_setting_subtitle": "Halda taustal ja esiplaanil üleslaadimise seadeid", "backward": "Tagasi", + "beta_sync": "Beeta sünkroonimise staatus", + "beta_sync_subtitle": "Halda uut sünkroonimissüsteemi", "biometric_auth_enabled": "Biomeetriline autentimine lubatud", "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", "biometric_no_options": "Biomeetrilisi valikuid ei ole", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tühjenda puhver", "cache_settings_clear_cache_button_title": "Tühjendab rakenduse puhvri. See mõjutab oluliselt rakenduse jõudlust, kuni puhver uuesti täidetakse.", "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", - "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt ignoreeritud", "cache_settings_duplicated_assets_title": "Dubleeritud üksused ({count})", "cache_settings_statistics_album": "Kogu pisipildid", "cache_settings_statistics_full": "Täismõõdus pildid", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Luba haptiline tagasiside", "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "hash_asset": "Arvuta üksuse räsi", + "hashed_assets": "Räsiga üksused", + "hashing": "Räsi arvutamine", "header_settings_add_header_tip": "Lisa päis", "header_settings_field_validator_msg": "Väärtus ei saa olla tühi", "header_settings_header_name_input": "Päise nimi", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Tund", "id": "ID", + "idle": "Jõude", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita üles Immich'i serverisse", "image": "Pilt", @@ -1165,7 +1171,9 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", + "local": "Lokaalne üksus", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", + "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", "local_network_sheet_info": "Rakendus ühendub valitud Wi-Fi võrgus olles serveriga selle URL-i kaudu", "location_permission": "Asukoha luba", @@ -1322,6 +1330,7 @@ "no_results": "Vasteid pole", "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", + "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", "not_in_any_album": "Pole üheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -1359,6 +1368,7 @@ "original": "originaal", "other": "Muud", "other_devices": "Muud seadmed", + "other_entities": "Muud objektid", "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", + "remote": "Kaugüksus", + "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", "remove_assets_shared_link_confirmation": "Kas oled kindel, et soovid eemaldada {count, plural, one {# üksuse} other {# üksust}} sellelt jagatud lingilt?", @@ -1556,11 +1568,15 @@ "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", "reset_pin_code": "Lähtesta PIN-kood", + "reset_sqlite": "Lähtesta SQLite andmebaas", + "reset_sqlite_confirmation": "Kas oled kindel, et soovid SQLite andmebaasi lähtestada? Andmete uuesti sünkroonimiseks pead välja ja jälle sisse logima", + "reset_sqlite_success": "SQLite andmebaas edukalt lähtestatud", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "Kõik duplikaadid lahendatud", "restore": "Taasta", "restore_all": "Taasta kõik", + "restore_trash_action_prompt": "{count} prügikastust taastatud", "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", @@ -1569,6 +1585,7 @@ "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", + "running": "Käimas", "save": "Salvesta", "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API võti salvestatud", @@ -1822,6 +1839,7 @@ "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", + "success": "Õnnestus", "suggestions": "Soovitused", "sunrise_on_the_beach": "Päikesetõus rannal", "support": "Tugi", @@ -1831,6 +1849,8 @@ "sync": "Sünkrooni", "sync_albums": "Sünkrooni albumid", "sync_albums_manual_subtitle": "Sünkrooni kõik üleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_local": "Sünkrooni lokaalsed üksused", + "sync_remote": "Sünkrooni kaugüksused", "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod üles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista üksuseid", @@ -1841,6 +1861,7 @@ "tag_updated": "Muudetud silt: {tag}", "tagged_assets": "{count, plural, one {# üksus} other {# üksust}} sildistatud", "tags": "Sildid", + "tap_to_run_job": "Puuduta tööte käivitamiseks", "template": "Mall", "theme": "Teema", "theme_selection": "Teema valik", diff --git a/i18n/fr.json b/i18n/fr.json index cebd4b694f..ff9f783771 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -406,6 +406,7 @@ "album_options": "Options de l'album", "album_remove_user": "Supprimer l'utilisateur ?", "album_remove_user_confirmation": "Êtes-vous sûr de vouloir supprimer {user} ?", + "album_search_not_found": "Aucun album trouvé ne correspond à votre recherche", "album_share_no_users": "Il semble que vous ayez partagé cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagé a de nouveaux médias", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordre de tri par défaut des albums", "albums_default_sort_order_description": "Ordre de tri des médias pour les nouveaux albums créés.", "albums_feature_description": "Bibliothèques de médias pouvant être partagés avec d'autres utilisateurs.", + "albums_on_device_count": "Album sur l'appareil ({count})", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", @@ -571,6 +573,8 @@ "backup_options_page_title": "Options de sauvegarde", "backup_setting_subtitle": "Ajuster les paramètres d'envoi au premier et en arrière-plan", "backward": "Arrière", + "beta_sync": "Statut de la synchronisation béta", + "beta_sync_subtitle": "Gérer le nouveau système de synchronisation", "biometric_auth_enabled": "Authentification biométrique activée", "biometric_locked_out": "L'authentification biométrique est verrouillé", "biometric_no_options": "Aucune option biométrique disponible", @@ -605,6 +609,7 @@ "cancel": "Annuler", "cancel_search": "Annuler la recherche", "canceled": "Annulé", + "canceling": "Annulation", "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", @@ -765,6 +770,7 @@ "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "deselect_all": "Tout désélectionner", "details": "Détails", "direction": "Ordre", "disabled": "Désactivé", @@ -839,6 +845,7 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_backup": "Activer Backup", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", @@ -1046,6 +1053,9 @@ "haptic_feedback_switch": "Activer le retour haptique", "haptic_feedback_title": "Retour haptique", "has_quota": "Quota", + "hash_asset": "Hasher le média", + "hashed_assets": "Média hashés", + "hashing": "Hash", "header_settings_add_header_tip": "Ajouter un en-tête", "header_settings_field_validator_msg": "Cette valeur ne peut pas être vide", "header_settings_header_name_input": "Nom de l'en-tête", @@ -1160,7 +1170,9 @@ "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des résultats échoué", + "local": "Local", "local_asset_cast_failed": "Impossible de caster un média qui n'a pas envoyé vers le serveur", + "local_assets": "Média locaux", "local_network": "Réseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connecté à ce réseau Wi-Fi", "location_permission": "Autorisation de localisation", @@ -1317,6 +1329,7 @@ "no_results": "Aucun résultat", "no_results_description": "Essayez un synonyme ou un mot-clé plus général", "no_shared_albums_message": "Créer un album pour partager vos photos et vidéos avec les personnes de votre réseau", + "no_uploads_in_progress": "Pas d'envoi en cours", "not_in_any_album": "Dans aucun album", "not_selected": "Non sélectionné", "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'étiquette de stockage aux médias précédemment envoyés, exécutez", @@ -1354,6 +1367,7 @@ "original": "original", "other": "Autre", "other_devices": "Autres appareils", + "other_entities": "Autres entités", "other_variables": "Autres variables", "owned": "Possédé", "owner": "Propriétaire", @@ -1485,6 +1499,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", + "queue_status": "File d'attente {count}/{total}", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1513,6 +1528,8 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", + "remote": "A distance", + "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", "remove_assets_shared_link_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de ce lien partagé ?", @@ -1550,11 +1567,15 @@ "reset_password": "Réinitialiser le mot de passe", "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", + "reset_sqlite": "Réinitialiser la base de données SQLite", + "reset_sqlite_confirmation": "Êtes vous sur que vous voulez réinitialiser la base de données SQLite ? Vous devrez vous déconnecter and vous reconnecter à nouveau pour re-synchroniser les données", + "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", "resolved_all_duplicates": "Résolution de tous les doublons", "restore": "Restaurer", "restore_all": "Tout restaurer", + "restore_trash_action_prompt": "{count} restauré de la corbeille", "restore_user": "Restaurer l'utilisateur", "restored_asset": "Média restauré", "resume": "Reprendre", @@ -1563,6 +1584,7 @@ "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", + "running": "En marche", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1816,6 +1838,7 @@ "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisé", "submit": "Soumettre", + "success": "Réussi", "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", "support": "Soutenir", @@ -1825,6 +1848,8 @@ "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", "sync_albums_manual_subtitle": "Synchroniser toutes les vidéos et photos envoyées dans les albums sélectionnés", + "sync_local": "Synchronisation locale", + "sync_remote": "Synchronisation à distance", "sync_upload_album_setting_subtitle": "Créez et envoyez vos photos et vidéos dans les albums sélectionnés sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les médias", @@ -1835,6 +1860,7 @@ "tag_updated": "Étiquette mise à jour : {tag}", "tagged_assets": "Étiquette ajoutée à {count, plural, one {# média} other {# médias}}", "tags": "Étiquettes", + "tap_to_run_job": "Appuyez pour démarrer la tâche", "template": "Modèle", "theme": "Thème", "theme_selection": "Sélection du thème", @@ -1915,6 +1941,7 @@ "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", "upload_concurrency": "Envois simultanés", + "upload_details": "Uploader les details", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", @@ -1965,6 +1992,7 @@ "view_album": "Afficher l'album", "view_all": "Voir tout", "view_all_users": "Voir tous les utilisateurs", + "view_details": "Voir les détails", "view_in_timeline": "Voir dans la vue chronologique", "view_link": "Voir le lien", "view_links": "Voir les liens", diff --git a/i18n/hu.json b/i18n/hu.json index c00fb37224..df995c0d6c 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -140,7 +140,7 @@ "machine_learning_smart_search_description": "Képek szemantikai keresése CLIP beágyazások segítségével", "machine_learning_smart_search_enabled": "Okos keresés engedélyezése", "machine_learning_smart_search_enabled_description": "Ha ki van kapcsolva, a képek nem lesznek átalakítva okos kereséshez.", - "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem válaszoló szervereket átmenetileg figyelmen kívül hagyja, amíg újra online nem lesznek.", + "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem elérhető szervereket átmenetileg figyelmen kívül lesznek hagyva, amíg újra online nem lesznek.", "manage_concurrency": "Párhuzamos Feladatok Kezelése", "manage_log_settings": "Naplózási beállítások kezelése", "map_dark_style": "Sötét stílus", @@ -166,6 +166,12 @@ "metadata_settings_description": "Metaadat beállítások kezelése", "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", + "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", + "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", + "nightly_tasks_generate_memories_setting": "Emlékek generálása", + "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", + "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", @@ -360,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", - "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő bélyegképeket. Ez a beállítás inkább a távoli képeket tölti be helyettük.", + "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő indexképeket. Ez a beállítás inkább a távoli képeket (a szerverről) tölti be helyettük.", "advanced_settings_prefer_remote_title": "Távoli képek előnyben részesítése", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejléceket, amiket az app elküldjön minden hálózati kérésnél", "advanced_settings_proxy_headers_title": "Proxy Fejlécek", @@ -517,7 +523,7 @@ "backup_controller_page_background_is_on": "Automatikus mentés a háttérben be van kapcsolva", "backup_controller_page_background_turn_off": "Háttérszolgáltatás kikapcsolása", "backup_controller_page_background_turn_on": "Háttérszolgáltatás bekapcsolása", - "backup_controller_page_background_wifi": "Csak WiFi-n", + "backup_controller_page_background_wifi": "Csak Wi-Fi-n", "backup_controller_page_backup": "Mentés", "backup_controller_page_backup_selected": "Kiválasztva: ", "backup_controller_page_backup_sub": "Mentett fotók és videók", @@ -551,8 +557,8 @@ "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", - "biometric_locked_out": "A biometrikus azonosításból kizárva", - "biometric_no_options": "A biometrikus azonosítás nem elérhető", + "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", + "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", @@ -642,7 +648,7 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", - "confirm_tag_face": "Szeretnéd ezt az arcot {name}-ként megjelölni?", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-nak/nek megjelölni?", "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", "connected_device": "Kapcsolt eszköz", "connected_to": "Kapcsolódva", @@ -770,7 +776,7 @@ "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", "download_sucess_android": "Média letöltve a DCIM/Immich mappába", - "download_waiting_to_retry": "Várakozás újrapróbálkozáshoz", + "download_waiting_to_retry": "Várás az újrapróbálkozásra", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -816,7 +822,7 @@ "enqueued": "Sorba állítva", "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", "enter_your_pin_code": "Add meg a jelszavad", - "enter_your_pin_code_subtitle": "Add meg a jelszavad a zárolt mappa megnyitásához", + "enter_your_pin_code_subtitle": "Add meg a PIN kódodat a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", @@ -916,7 +922,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", - "unable_to_reset_pin_code": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "PIN kód visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -988,7 +994,7 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", - "filter_places": "Helyek szűrése", + "filter_places": "Helyszínek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", "folder": "Mappa", @@ -1029,9 +1035,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, kihagyás", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, ki lesznek hagyva", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, kihagyás", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, ki lesznek hagyva", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -1040,7 +1046,7 @@ "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", - "home_page_locked_error_local": "Helyi elemek nem mozgathatóak a zárolt mappába, átugorva", + "home_page_locked_error_local": "A Helyi elemek nem mozgathatóak a zárolt mappába, ki lesznek hagyva", "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", @@ -1087,10 +1093,10 @@ "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", - "ios_debug_info_no_processes_queued": "Nincs sorba állított hátterfolyamat", + "ios_debug_info_no_processes_queued": "Nincs a sorban háttérfolyamat jelenleg", "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", - "ios_debug_info_processing_ran_at": "Feldolgozás futott {dateTime}", + "ios_debug_info_processing_ran_at": "A feldolgozás ekkor futott: {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1099,7 +1105,7 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", - "language_no_results_subtitle": "Próbáld a keresésed módosítását", + "language_no_results_subtitle": "Próbáld módosítani a szavaidat a keresésnél", "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", @@ -1129,7 +1135,7 @@ "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "A Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", @@ -1139,7 +1145,7 @@ "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", - "logged_in_as": "{user}-ként belépve", + "logged_in_as": "Belépve: {user} néven", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1234,9 +1240,9 @@ "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", "move": "Áthelyezés", - "move_off_locked_folder": "Zárolt mappából kivonás", + "move_off_locked_folder": "Átmozgatás a zárolt mappából", "move_to_locked_folder": "Áthelyezés a zárolt mappába", - "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappából lesznek elérhetőek", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappában lesznek elérhetőek", "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", @@ -1254,7 +1260,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", - "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót az oldal biztosítására", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót a mappa biztonságos eléréséhez", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1283,7 +1289,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", - "nothing_here_yet": "Itt még nincs semmi", + "nothing_here_yet": "Még semmi sincs itt", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1293,7 +1299,7 @@ "notifications_setting_description": "Értesítések kezelése", "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", - "offline": "Offline", + "offline": "Nem elérhető (offline)", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", @@ -1303,7 +1309,7 @@ "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", - "online": "Online", + "online": "Online (elérhető)", "only_favorites": "Csak kedvencek", "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", @@ -1754,7 +1760,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", - "stacktrace": "Hibaleírás", + "stacktrace": "Hiba leírása", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1882,7 +1888,7 @@ "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", - "user_privacy": "Felhasználói biztonság", + "user_privacy": "Felhasználói adatvédelem", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", diff --git a/i18n/id.json b/i18n/id.json index ff5e2524e2..92cb07ff30 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -169,15 +169,23 @@ "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, kadaluarsa dari database", "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_missing_thumbnails_setting": "Membuat thumbnail yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Mengantrikan aset tanpa thumbnail untuk pembuatan thumbnail", + "nightly_tasks_settings": "Pengaturan Tugas Malam", + "nightly_tasks_settings_description": "Atur tugas malam", "nightly_tasks_start_time_setting": "Waktu mulai", + "nightly_tasks_start_time_setting_description": "Waktu saat server mulai menjalankan tugas malam", + "nightly_tasks_sync_quota_usage_setting": "Sinkronisasi penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Pembaruan kuota penyimpanan pengguna, berdasarkan penggunaan sekarang", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email.", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", diff --git a/i18n/it.json b/i18n/it.json index 5e12bdfc97..0aec538a85 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -406,6 +406,7 @@ "album_options": "Impostazioni Album", "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", + "album_search_not_found": "Nessun album trovato corrispondente alla tua ricerca", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordinamento predefinito degli album", "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", + "albums_on_device_count": "Album sul dispositivo ({count})", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", @@ -605,6 +607,7 @@ "cancel": "Annulla", "cancel_search": "Annulla ricerca", "canceled": "Annullato", + "canceling": "Annullamento", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -751,6 +754,7 @@ "delete_key": "Elimina chiave", "delete_library": "Elimina libreria", "delete_link": "Elimina link", + "delete_local_action_prompt": "{count} elementi rimossi in locale", "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", @@ -764,6 +768,7 @@ "description": "Descrizione", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "deselect_all": "Deseleziona Tutto", "details": "Dettagli", "direction": "Direzione", "disabled": "Disabilitato", @@ -781,6 +786,7 @@ "documentation": "Documentazione", "done": "Fatto", "download": "Scarica", + "download_action_prompt": "Scaricando {count} elementi", "download_canceled": "Download annullato", "download_complete": "Download completato", "download_enqueue": "Download in coda", @@ -837,6 +843,7 @@ "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", + "enable_backup": "Abilita Backup", "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", @@ -1483,6 +1490,7 @@ "purchase_server_description_2": "Stato di Contributore", "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", + "queue_status": "Messi in coda {count}/{total}", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", "rating_count": "{count, plural, one {# stella} other {# stelle}}", @@ -1518,6 +1526,7 @@ "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", + "remove_from_album_action_prompt": "{count} elementi rimossi dall'album", "remove_from_favorites": "Rimuovi dai preferiti", "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", @@ -1691,6 +1700,7 @@ "settings_saved": "Impostazioni salvate", "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", + "share_action_prompt": "Condivisi {count} elementi", "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparo…", @@ -1792,6 +1802,7 @@ "sort_title": "Titolo", "source": "Fonte", "stack": "Raggruppa", + "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", "stack_selected_photos": "Impila foto selezionate", @@ -1880,6 +1891,7 @@ "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", + "unarchive_action_prompt": "{count} elementi rimossi dall'Archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", diff --git a/i18n/lv.json b/i18n/lv.json index 5af3e0511c..2e6b702e22 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -707,6 +707,9 @@ "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", + "no_albums_with_name_yet": "Izskatās, ka tev vēl nav albumu ar šādu nosaukumu.", + "no_albums_yet": "Izskatās, ka tev vēl nav neviena albuma.", + "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKŠĶINIET, LAI AUGŠUPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktīvu", "no_duplicates_found": "Dublikāti netika atrasti.", @@ -730,6 +733,10 @@ "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", "ok": "Labi", + "onboarding": "Uzņemšana", + "onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainīt iestatījumos.", + "onboarding_theme_description": "Izvēlies savas instances krāsu motīvu. To vēlāk var mainīt iestatījumos.", + "onboarding_user_welcome_description": "Sāksim darbu!", "online": "Tiešsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -737,13 +744,16 @@ "open_the_search_filters": "Atvērt meklēšanas filtrus", "options": "Iestatījumi", "or": "vai", + "organize_your_library": "Bibliotēkas organizēšana", "original": "oriģināls", "other": "Citi", "other_devices": "Citas ierīces", "other_variables": "Citi mainīgie", "owned": "Īpašumā", "owner": "Īpašnieks", + "partner": "Partneris", "partner_can_access": "{partner} var piekļūt", + "partner_can_access_location": "Fotogrāfiju uzņemšanas vieta", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "Apskatīt visu", "partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.", @@ -757,6 +767,7 @@ "password_does_not_match": "Parole nesakrīt", "path": "Ceļš", "pause": "Pauzēt", + "pause_memories": "Pauzēt atmiņas", "paused": "Nopauzēts", "people": "Cilvēki", "permission_onboarding_back": "Atpakaļ", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 89b5e298bc..2abc08e7f2 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", "backward": "Bakover", + "beta_sync": "Beta synkroniseringsstatus", + "beta_sync_subtitle": "Håndter det nye synkroniseringssystemet", "biometric_auth_enabled": "Biometrisk autentisering aktivert", "biometric_locked_out": "Du er låst ute av biometrisk verifisering", "biometric_no_options": "Ingen biometriske valg tilgjengelige", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", - "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", + "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", "has_quota": "Kvote", + "hash_asset": "Hash objekter", + "hashed_assets": "Hashede objekter", + "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1083,6 +1088,7 @@ "host": "Vert", "hour": "Time", "id": "ID", + "idle": "Uvirksom", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret på iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke å laste inn søkeresultater", + "local": "Lokal", "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale objekter", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en når du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1322,6 +1330,7 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", + "no_uploads_in_progress": "Ingen opplasting pågår", "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -1359,6 +1368,7 @@ "original": "original", "other": "Annet", "other_devices": "Andre enheter", + "other_entities": "Andre objekter", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Oppdaterer ansikter", "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", + "remote": "Eksternt", + "remote_assets": "Eksterne objekter", "remove": "Fjern", "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", @@ -1556,11 +1568,15 @@ "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", "reset_pin_code": "Resett PINkode", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Er du sikker på at du vil resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", + "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", + "restore_trash_action_prompt": "{count} gjenopprettet fra søppelbøtten", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", + "running": "Kjører", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", "saved_api_key": "Lagret API-nøkkel", @@ -1822,6 +1839,7 @@ "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", + "success": "Vellykket", "suggestions": "Forslag", "sunrise_on_the_beach": "Soloppgang på stranden", "support": "Støtte", @@ -1831,6 +1849,8 @@ "sync": "Synkroniser", "sync_albums": "Synkroniser albumer", "sync_albums_manual_subtitle": "Synkroniser alle opplastede videoer og bilder til det valgte backupalbumet", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", "sync_upload_album_setting_subtitle": "Opprett og last opp dine bilder og videoer til det valgte albumet på Immich", "tag": "Tagg", "tag_assets": "Merk ressurser", @@ -1841,6 +1861,7 @@ "tag_updated": "Oppdater merke: {tag}", "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", "tags": "Merker", + "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", diff --git a/i18n/nl.json b/i18n/nl.json index 2cedd63b15..86cb0fba6e 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -426,6 +426,7 @@ "albums_default_sort_order": "Standaard sorteervolgorde album", "albums_default_sort_order_description": "Initiële sorteervolgorde bij het maken van nieuwe albums.", "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", + "albums_on_device_count": "Albums op apparaat ({count})", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -572,6 +573,8 @@ "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", "backward": "Achteruit", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Beheer het nieuwe synchronisatiesysteem", "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", "biometric_locked_out": "Biometrische authenticatie is vergrendeld", "biometric_no_options": "Geen biometrische opties beschikbaar", @@ -589,7 +592,7 @@ "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", - "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", + "cache_settings_duplicated_assets_subtitle": "Foto’s en video's die de app negeert", "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", "cache_settings_statistics_album": "Bibliotheekthumbnails", "cache_settings_statistics_full": "Volledige afbeeldingen", @@ -1050,6 +1053,9 @@ "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", + "hash_asset": "Hash asset", + "hashed_assets": "Gehashte assets", + "hashing": "Hashen", "header_settings_add_header_tip": "Header toevoegen", "header_settings_field_validator_msg": "Waarde kan niet leeg zijn", "header_settings_header_name_input": "Header naam", @@ -1082,6 +1088,7 @@ "host": "Host", "hour": "Uur", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1164,7 +1171,9 @@ "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", + "local": "Lokaal", "local_asset_cast_failed": "Kan geen asset casten die nog niet geüpload is naar de server", + "local_assets": "Lokale Assets", "local_network": "Lokaal netwerk", "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatietoestemming", @@ -1321,6 +1330,7 @@ "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", + "no_uploads_in_progress": "Geen uploads bezig", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -1358,6 +1368,7 @@ "original": "origineel", "other": "Overige", "other_devices": "Andere apparaten", + "other_entities": "Andere entities", "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", @@ -1518,6 +1529,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1555,11 +1568,15 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moetenn uitloggen om de data opnieuw te synchroniseren.", + "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", + "restore_trash_action_prompt": "{count} teruggezet uit prullenbak", "restore_user": "Gebruiker herstellen", "restored_asset": "Asset hersteld", "resume": "Hervatten", @@ -1568,6 +1585,7 @@ "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", + "running": "Actief", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", "saved_api_key": "API-sleutel opgeslagen", @@ -1821,6 +1839,7 @@ "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", + "success": "Succes", "suggestions": "Suggesties", "sunrise_on_the_beach": "Zonsopkomst op het strand", "support": "Ondersteuning", @@ -1830,6 +1849,8 @@ "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geüploade video’s en foto’s naar de geselecteerde back-up albums", + "sync_local": "Lokaal synchroniseren", + "sync_remote": "Op afstand synchroniseren", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", "tag": "Tag", "tag_assets": "Assets taggen", @@ -1840,6 +1861,7 @@ "tag_updated": "Tag bijgewerkt: {tag}", "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", "tags": "Tags", + "tap_to_run_job": "Klik om job te starten", "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", diff --git a/i18n/ru.json b/i18n/ru.json index 84db991027..c0a01b0be2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -228,7 +228,7 @@ "password_settings_description": "Управление настройками входа по паролю", "paths_validated_successfully": "Все пути успешно прошли проверку", "person_cleanup_job": "Очистка персоны", - "quota_size_gib": "Размер квоты (ГБ)", + "quota_size_gib": "Размер квоты (GiB)", "refreshing_all_libraries": "Обновление всех библиотек", "registration": "Регистрация администратора", "registration_description": "Первый зарегистрированный пользователь будет назначен администратором. В дальнейшем этой учетной записи будет доступно создание дополнительных пользователей и управление сервером.", @@ -241,7 +241,7 @@ "server_external_domain_settings": "Внешний домен", "server_external_domain_settings_description": "Домен для публичных ссылок, включая http(s)://", "server_public_users": "Публичные пользователи", - "server_public_users_description": "Отображать всех пользователей (имена и email) для добавления в общие альбомы. Когда отключено, список пользователей будет доступен только администраторам.", + "server_public_users_description": "Выводить список пользователей (имена и email) в общих альбомах. Когда отключено, список доступен только администраторам, пользователи смогут делиться только ссылкой.", "server_settings": "Настройки сервера", "server_settings_description": "Управление настройками сервера", "server_welcome_message": "Приветственное сообщение", @@ -407,7 +407,7 @@ "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", "album_search_not_found": "Не найдено альбомов по вашему запросу", - "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", + "album_share_no_users": "Нет доступных пользователей, с которыми можно поделиться альбомом.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", "album_user_left": "Вы покинули {album}", @@ -433,8 +433,8 @@ "all_videos": "Все видео", "allow_dark_mode": "Разрешить темный режим", "allow_edits": "Разрешить редактирование", - "allow_public_user_to_download": "Разрешить скачивание публичным пользователям", - "allow_public_user_to_upload": "Разрешить публичным пользователям загружать файлы", + "allow_public_user_to_download": "Разрешить скачивание", + "allow_public_user_to_upload": "Разрешить добавление файлов", "alt_text_qr_code": "QR-код", "anti_clockwise": "Против часовой", "api_key": "API ключ", @@ -573,6 +573,8 @@ "backup_options_page_title": "Резервное копирование", "backup_setting_subtitle": "Настройка активного и фонового резервного копирования", "backward": "Назад", + "beta_sync": "Статус бета-синхронизации", + "beta_sync_subtitle": "Управление новой системой синхронизации", "biometric_auth_enabled": "Биометрическая аутентификация включена", "biometric_locked_out": "Вам закрыт доступ к биометрической аутентификации", "biometric_no_options": "Биометрическая аутентификация недоступна", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Очистить кэш", "cache_settings_clear_cache_button_title": "Очищает кэш приложения. Это негативно повлияет на производительность, пока кэш не будет создан заново.", "cache_settings_duplicated_assets_clear_button": "ОЧИСТИТЬ", - "cache_settings_duplicated_assets_subtitle": "Фото и видео, занесенные приложением в черный список", + "cache_settings_duplicated_assets_subtitle": "Фото и видео, пропускаемые приложением", "cache_settings_duplicated_assets_title": "Дублирующиеся объекты ({count})", "cache_settings_statistics_album": "Миниатюры библиотеки", "cache_settings_statistics_full": "Полные изображения", @@ -616,7 +618,7 @@ "change_date": "Изменить дату", "change_description": "Изменить описание", "change_display_order": "Изменить порядок отображения", - "change_expiration_time": "Изменить время окончания", + "change_expiration_time": "Изменить срок действия", "change_location": "Изменить местоположение", "change_name": "Изменить имя", "change_name_successfully": "Имя успешно изменено", @@ -692,7 +694,7 @@ "copy_link": "Копировать ссылку", "copy_link_to_clipboard": "Скопировать ссылку в буфер обмена", "copy_password": "Скопировать пароль", - "copy_to_clipboard": "Скопировать в буфер обмена", + "copy_to_clipboard": "Скопировать настройки в буфер обмена", "country": "Страна", "cover": "Обложка", "covers": "Обложки", @@ -830,7 +832,7 @@ "edit_people": "Редактировать людей", "edit_tag": "Изменить тег", "edit_title": "Редактировать Заголовок", - "edit_user": "Редактирование пользователя", + "edit_user": "Изменить пользователя", "edited": "Отредактировано", "editor": "Редактор", "editor_close_without_save_prompt": "Изменения не будут сохранены", @@ -994,7 +996,7 @@ "experimental_settings_subtitle": "Используйте на свой страх и риск!", "experimental_settings_title": "Экспериментальные функции", "expire_after": "Истекает через", - "expired": "Срок действия истек", + "expired": "Срок действия истёк", "expires_date": "Срок действия до {date}", "explore": "Поиск", "explorer": "Проводник", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Включить тактильную отдачу", "haptic_feedback_title": "Тактильная отдача", "has_quota": "Квота", + "hash_asset": "Хешированный объект", + "hashed_assets": "Хешированные объекты", + "hashing": "Хеширование", "header_settings_add_header_tip": "Добавить заголовок", "header_settings_field_validator_msg": "Значение не может быть пустым", "header_settings_header_name_input": "Имя заголовка", @@ -1083,6 +1088,7 @@ "host": "Хост", "hour": "Час", "id": "ID", + "idle": "В ожидании", "ignore_icloud_photos": "Пропускать файлы из iCloud", "ignore_icloud_photos_description": "Не загружать файлы в Immich, если они хранятся в iCloud", "image": "Изображения", @@ -1109,8 +1115,8 @@ "include_archived": "Отображать архив", "include_shared_albums": "Включать общие альбомы", "include_shared_partner_assets": "Включать общие ресурсы партнера", - "individual_share": "Персональный доступ", - "individual_shares": "Индивидуальный доступ", + "individual_share": "Индивидуальная подборка", + "individual_shares": "Подборки", "info": "Информация", "interval": { "day_at_onepm": "Каждый день в 13:00", @@ -1165,7 +1171,9 @@ "list": "Список", "loading": "Загрузка", "loading_search_results_failed": "Загрузка результатов поиска не удалась", + "local": "На устройстве", "local_asset_cast_failed": "Невозможно транслировать объект, который ещё не загружен на сервер", + "local_assets": "Объекты на устройстве", "local_network": "Локальная сеть", "local_network_sheet_info": "Приложение будет подключаться к серверу по этому адресу, когда устройство подключено к выбранной Wi-Fi сети", "location_permission": "Доступ к местоположению", @@ -1322,6 +1330,7 @@ "no_results": "Нет результатов", "no_results_description": "Попробуйте использовать синоним или более общее ключевое слово", "no_shared_albums_message": "Создайте альбом для обмена фотографиями и видеозаписями с людьми в вашей сети", + "no_uploads_in_progress": "Нет активных загрузок", "not_in_any_album": "Ни в одном альбоме", "not_selected": "Не выбрано", "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным ресурсам, запустите", @@ -1359,6 +1368,7 @@ "original": "оригинал", "other": "Другое", "other_devices": "Другие устройства", + "other_entities": "Другие объекты", "other_variables": "Другие переменные", "owned": "Мои", "owner": "Владелец", @@ -1456,7 +1466,7 @@ "profile_drawer_server_out_of_date_minor": "Версия сервера устарела. Пожалуйста, обновите его.", "profile_image_of_user": "Изображение профиля {user}", "profile_picture_set": "Фото профиля установлено.", - "public_album": "Публичный альбом", + "public_album": "Общий альбом", "public_share": "Публичный доступ", "purchase_account_info": "Поддержка", "purchase_activated_subtitle": "Благодарим вас за поддержку Immich и программного обеспечения с открытым исходным кодом", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Обновление лиц", "refreshing_metadata": "Обновление метаданных", "regenerating_thumbnails": "Восстановление миниатюр", + "remote": "На сервере", + "remote_assets": "Объекты на сервере", "remove": "Удалить", "remove_assets_album_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из альбома?", "remove_assets_shared_link_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из публичного доступа по этой ссылке?", @@ -1556,11 +1568,15 @@ "reset_password": "Сброс пароля", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", + "reset_sqlite": "Очистить базу данных SQLite", + "reset_sqlite_confirmation": "Вы уверены, что хотите очистить базу данных SQLite? Вам потребуется выйти из системы и снова войти для повторной синхронизации данных.", + "reset_sqlite_success": "База данных SQLite успешно очищена", "reset_to_default": "Восстановление значений по умолчанию", "resolve_duplicates": "Устранить дубликаты", "resolved_all_duplicates": "Все дубликаты устранены", "restore": "Восстановить", "restore_all": "Восстановить все", + "restore_trash_action_prompt": "{count} восстановлено из корзины", "restore_user": "Восстановить пользователя", "restored_asset": "Восстановленный объект", "resume": "Продолжить", @@ -1569,6 +1585,7 @@ "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", + "running": "Выполняется", "save": "Сохранить", "save_to_gallery": "Сохранить в галерею", "saved_api_key": "API ключ изменён", @@ -1822,6 +1839,7 @@ "storage_quota": "Квота хранилища", "storage_usage": "{used} из {available}", "submit": "Подтвердить", + "success": "Успешно", "suggestions": "Предложения", "sunrise_on_the_beach": "Восход солнца на пляже", "support": "Поддержка", @@ -1831,6 +1849,8 @@ "sync": "Синхр.", "sync_albums": "Синхронизировать альбомы", "sync_albums_manual_subtitle": "Синхронизировать все загруженные фото и видео в выбранные альбомы для резервного копирования", + "sync_local": "Синхронизировать локально", + "sync_remote": "Синхронизация с сервером", "sync_upload_album_setting_subtitle": "Создавайте и загружайте свои фотографии и видео в выбранные альбомы на сервер Immich", "tag": "Тег", "tag_assets": "Добавить теги", @@ -1841,6 +1861,7 @@ "tag_updated": "Тег {tag} изменен", "tagged_assets": "Тег назначен для {count, plural, one {# объекта} other {# объектов}}", "tags": "Теги", + "tap_to_run_job": "Нажмите для запуска задачи", "template": "Шаблон", "theme": "Тема", "theme_selection": "Выбор темы", diff --git a/i18n/sk.json b/i18n/sk.json index c8c1cb5303..a29262a6e8 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -45,14 +45,14 @@ "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", "backup_settings": "Nastavenia výpisu databázy", - "backup_settings_description": "Správa nastavení výpisu databázy.", + "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", - "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete obnoviť heslo pre {user}?", "confirm_user_pin_code_reset": "Ste si istí, že chcete opätovne nastaviť PIN kód používateľa {user}?", "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", @@ -61,10 +61,10 @@ "disable_login": "Zakázať prihlásenie", "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", - "external_library_management": "Správa Externej Knižnice", + "external_library_management": "Spravovanie externej knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Resetovať“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Aktualizovať“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -94,7 +94,7 @@ "job_not_concurrency_safe": "Táto úloha nie je bezpečná pre súbežné spracovanie.", "job_settings": "Úlohy", "job_settings_description": "Spravovať súbežnosť úloh", - "job_status": "Stav Úloh", + "job_status": "Stav úloh", "jobs_delayed": "{jobCount, plural, one {# oneskorený} few {# oneskorené} other {# oneskorených}}", "jobs_failed": "{jobCount, plural, one {# neúspešný} few {# neúspešné} other {# neúspešných}}", "library_created": "Vytvorená knižnica: {library}", @@ -141,15 +141,15 @@ "machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie", "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", - "manage_concurrency": "Správa súbežnosti", + "manage_concurrency": "Spravovať súbežnosť", "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", "map_gps_settings": "Mapa a nastavenia GPS", - "map_gps_settings_description": "Spravujte nastavenia mapy a GPS (reverzné geokódovanie)", + "map_gps_settings_description": "Spravovať nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", - "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", + "map_manage_reverse_geocoding_settings": "Spravovať nastavenia reverzného geokódovania", "map_reverse_geocoding": "Reverzné Geokódovanie", "map_reverse_geocoding_enable_description": "Povoliť reverzné geokódovanie", "map_reverse_geocoding_settings": "Reverzné geokódovanie", @@ -214,7 +214,7 @@ "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", - "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", + "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na dokumentáciu.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", @@ -234,7 +234,7 @@ "registration_description": "Keďže ste prvým používateľom v systéme, budú vám pridelené správcovské práva na vykonávanie všetkých úloh a vrátane tvorby nových používateľov.", "require_password_change_on_login": "Vyžadovať od používateľa zmenu hesla pri prvom prihlásení", "reset_settings_to_default": "Obnoviť pôvodné nastavenia", - "reset_settings_to_recent_saved": "Obnoviť naposledy uložené nastavenia", + "reset_settings_to_recent_saved": "Nastavenia boli obnovené na posledné uložené nastavenia", "scanning_library": "Knižnica sa skenuje", "search_jobs": "Vyhľadať úlohy…", "send_welcome_email": "Odoslať uvítací e-mail", @@ -259,7 +259,7 @@ "storage_template_migration_description": "Použite aktuálnu {template} na predtým nahrané médiá", "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", - "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_more_details": "Podrobnejšie informácie o tejto funkcii nájdete v časti šablóna úložiska a jej následky", "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", @@ -278,9 +278,9 @@ "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", "theme_custom_css_settings_description": "CSS štýly umožňujú prispôsobiť dizajn Immich.", - "theme_settings": "Motívy", + "theme_settings": "Nastavenia témy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", - "thumbnail_generation_job": "Generovať Miniatúry", + "thumbnail_generation_job": "Generovať miniatúry", "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", @@ -300,7 +300,7 @@ "transcoding_bitrate_description": "Videá presahujúce maximálnu bitovú rýchlosť alebo videá, ktoré nie sú v akceptovanom formáte", "transcoding_codecs_learn_more": "Ak sa chcete dozvedieť viac o tu použitej terminológii, pozrite si dokumentáciu FFmpeg pre kodek H.264, kodek HEVC a VP9 kodek.", "transcoding_constant_quality_mode": "Režim konštantnej kvality", - "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované funkciou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", @@ -321,13 +321,13 @@ "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", - "transcoding_preset_preset": "Prednastavenie (-preset)", + "transcoding_preset_preset": "Predvoľba (-preset)", "transcoding_preset_preset_description": "Rýchlosť kompresie. Pomalšie predvoľby vytvárajú menšie súbory a zvyšujú kvalitu, keď sa zameriavajú na určitý dátový tok. VP9 ignoruje rýchlosti vyššie ako „rýchlejšie“.", "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", "transcoding_settings": "Nastavenia prekódovania videa", - "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", + "transcoding_settings_description": "Spravovať, ktoré videá sa majú prekódovať a ako sa majú spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", "transcoding_temporal_aq": "Časové AQ", @@ -349,13 +349,13 @@ "trash_settings_description": "Spravovať nastavenia koša", "user_cleanup_job": "Premazanie používateľov", "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", - "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a médií používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_delay_settings": "Oneskorenie vymazania", + "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", - "user_management": "Správa používateľov", - "user_password_has_been_reset": "Heslo používateľa bolo resetované:", + "user_management": "Spravovanie používateľov", + "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", @@ -393,7 +393,7 @@ "age_year_months": "Vek 1 rok, {months, plural, one {# month} other {# months}}", "age_years": "{years, plural, other {Vek #}}", "album_added": "Album bol pridaný", - "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď ste pridaní do zdieľaného albumu", + "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď vás pridajú do zdieľaného albumu", "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti zálohovania", "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", "backward": "Dozadu", + "beta_sync": "Stav synchronizácie verzie Beta", + "beta_sync_subtitle": "Spravovať nový systém synchronizácie", "biometric_auth_enabled": "Biometrické overovanie je povolené", "biometric_locked_out": "Ste vymknutí z biometrického overovania", "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", - "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videá, ktoré aplikácia ignoruje podľa zoznamu", "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", @@ -628,7 +630,7 @@ "change_password_form_password_mismatch": "Heslá sa nezhodujú", "change_password_form_reenter_new_password": "Znova zadajte nové heslo", "change_pin_code": "Zmeniť PIN kód", - "change_your_password": "Zmeňte si heslo", + "change_your_password": "Zmeniť heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", @@ -723,7 +725,7 @@ "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", - "dark": "Tmavý", + "dark": "Tmavá", "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", @@ -949,7 +951,7 @@ "unable_to_remove_library": "Nie je možné odstrániť knižnicu", "unable_to_remove_partner": "Nie je možné odstrániť partnera", "unable_to_remove_reaction": "Nie je možné odstrániť reakciu", - "unable_to_reset_password": "Nie je možné resetovať heslo", + "unable_to_reset_password": "Nie je možné obnoviť heslo", "unable_to_reset_pin_code": "Nie je možné obnoviť PIN kód", "unable_to_resolve_duplicate": "Nie je možné vyriešiť duplikát", "unable_to_restore_assets": "Nie je možné obnoviť položky", @@ -987,7 +989,7 @@ "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", - "exit_slideshow": "Opustiť Slideshow", + "exit_slideshow": "Opustiť prezentáciu", "expand_all": "Rozbaliť všetko", "experimental_settings_new_asset_list_subtitle": "Prebiehajúca práca", "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "hash_asset": "Hashovať položku", + "hashed_assets": "Hashované položky", + "hashing": "Hashovanie", "header_settings_add_header_tip": "Pridať hlavičku", "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", "header_settings_header_name_input": "Názov hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "idle": "Nečinné", "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", @@ -1139,7 +1145,7 @@ "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", "language_no_results_title": "Neboli nájdené žiadne jazyky", "language_search_hint": "Vyhľadať jazyky...", - "language_setting_description": "Vyberte preferovaný jazyk", + "language_setting_description": "Vyberte požadovaný jazyk", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1156,7 +1162,7 @@ "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", "licenses": "Licencie", - "light": "Svetlý", + "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", "link_options": "Možnosti odkazu", @@ -1165,7 +1171,9 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local": "Lokálne", "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", + "local_assets": "Lokálne položky", "local_network": "Miestna sieť", "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", "location_permission": "Povolenie na určenie polohy", @@ -1277,7 +1285,7 @@ "move_off_locked_folder": "Presunúť zo zamknutého priečinka", "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", - "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odobrané zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", @@ -1322,6 +1330,7 @@ "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", + "no_uploads_in_progress": "Žiadne prebiehajúce nahrávanie", "not_in_any_album": "Nie je v žiadnom albume", "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", @@ -1343,7 +1352,7 @@ "onboarding": "Na palube", "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", - "onboarding_server_welcome_description": "Nastavme vašu inštanciu pomocou bežných nastavení.", + "onboarding_server_welcome_description": "Poďme si nastaviť vašu inštanciu s niekoľkými bežnými nastaveniami.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatné", "other_devices": "Ďalšie zariadenia", + "other_entities": "Ostatné subjekty", "other_variables": "Ostatné premenné", "owned": "Vlastnené", "owner": "Vlastník", @@ -1434,9 +1444,9 @@ "play_or_pause_video": "Pustí alebo pozastaví video", "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_subtitle": "Spravujte predvoľby aplikácie", + "preferences_settings_subtitle": "Spravovať predvoľby aplikácie", "preferences_settings_title": "Predvoľby", - "preset": "Prednastavenie", + "preset": "Predvoľba", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", @@ -1508,7 +1518,7 @@ "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", - "refresh": "Obnoviť", + "refresh": "Aktualizovať", "refresh_encoded_videos": "Obnoviť enkódované videá", "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", + "remote": "Vzdialené", + "remote_assets": "Vzdialené položky", "remove": "Odstrániť", "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", @@ -1529,8 +1541,8 @@ "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", - "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", - "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", + "remove_from_locked_folder": "Odobrať zo zamknutého priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá odobrať zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", "remove_memory": "Odstrániť spomienku", "remove_photo_from_memory": "Odstrániť fotografiu z tejto spomienky", @@ -1552,28 +1564,33 @@ "require_password": "Vyžadovať heslo", "require_user_to_change_password_on_first_login": "Vyžadovať zmenu hesla po prvom prihlásení", "rescan": "Opätovné vyhľadávanie", - "reset": "Resetovať", + "reset": "Obnoviť", "reset_password": "Obnoviť heslo", - "reset_people_visibility": "Resetovať viditeľnosť ľudí", + "reset_people_visibility": "Obnoviť viditeľnosť ľudí", "reset_pin_code": "Obnoviť PIN kód", - "reset_to_default": "Resetovať na predvolené", + "reset_sqlite": "Obnoviť SQLite databázu", + "reset_sqlite_confirmation": "Ste si istí, že chcete obnoviť SQLite databázu? Na opätovnú synchronizáciu údajov sa budete musieť odhlásiť a znova prihlásiť", + "reset_sqlite_success": "Úspešné obnovenie databázy SQLite", + "reset_to_default": "Obnoviť na predvolené", "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", "restore_all": "Navrátit všetko", + "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", "restored_asset": "Navrátené položky", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", - "review_duplicates": "Prezrieť duplikáty", + "review_duplicates": "Preskúmať duplikáty", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Spustené", "save": "Uložiť", "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", - "saved_settings": "Uložené nastavenia", + "saved_settings": "Nastavenia boli uložené", "say_something": "Napíšte niečo", "scaffold_body_error_occurred": "Vyskytla sa chyba", "scan_all_libraries": "Preskenovať všetky knižnice", @@ -1585,7 +1602,7 @@ "search_by_context": "Hľadať s kontextom", "search_by_description": "Vyhľadávanie podľa popisu", "search_by_description_example": "Pešia turistika v Sape", - "search_by_filename": "Hľadať s názvom alebo príponou súboru", + "search_by_filename": "Hľadať podľa názvu alebo prípony súboru", "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "Hľadať značku fotoaparátu...", "search_camera_model": "Hľadať model fotoaparátu...", @@ -1809,7 +1826,7 @@ "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", - "start_date": "Začiatočný dátum", + "start_date": "Počiatočný dátum", "state": "Štát", "status": "Stav", "stop_casting": "Zastaviť prenos", @@ -1822,6 +1839,7 @@ "storage_quota": "Úložný limit", "storage_usage": "Využitých {used} z {available}", "submit": "Odoslať", + "success": "Úspech", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slnka na pláži", "support": "Podpora", @@ -1831,9 +1849,11 @@ "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_local": "Synchronizovať lokálne", + "sync_remote": "Synchronizovať vzdialené", "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", "tag": "Štítok", - "tag_assets": "Označiť položky", + "tag_assets": "Pridať štítky", "tag_created": "Vytvorený štítok: {tag}", "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", @@ -1841,6 +1861,7 @@ "tag_updated": "Upravený štítok: {tag}", "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", + "tap_to_run_job": "Ťuknutím na položku spustíte úlohu", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", @@ -1863,7 +1884,7 @@ "time_based_memories": "Časové spomienky", "timeline": "Časová os", "timezone": "Časové pásmo", - "to_archive": "Archív", + "to_archive": "Archivovať", "to_change_password": "Zmeniť heslo", "to_favorite": "Obľúbiť", "to_login": "Prihlásiť", @@ -1898,7 +1919,7 @@ "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", - "unknown_country": "Neznámy štát", + "unknown_country": "Neznáma krajina", "unknown_year": "Neznámy rok", "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", @@ -1986,7 +2007,7 @@ "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", - "waiting": "Čaká", + "waiting": "Čakajúce", "warning": "Varovanie", "week": "Týždeň", "welcome": "Vitajte", @@ -1996,7 +2017,7 @@ "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", - "you_dont_have_any_shared_links": "Nemáte žiadne zdielané linky", + "you_dont_have_any_shared_links": "Nemáte žiadne zdielané odkazy", "your_wifi_name": "Váš názov siete Wi-Fi", "zoom_image": "Priblížiť obrázok" } diff --git a/i18n/sl.json b/i18n/sl.json index c4409c717e..e08a9dd506 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", "backward": "Nazaj", + "beta_sync": "Stanje sinhronizacije beta različice", + "beta_sync_subtitle": "Upravljanje novega sistema sinhronizacije", "biometric_auth_enabled": "Biometrična avtentikacija omogočena", "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", "biometric_no_options": "Biometrične možnosti niso na voljo", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Počisti predpomnilnik", "cache_settings_clear_cache_button_title": "Počisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na črni seznam", + "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki so prezrti s strani aplikacije", "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", "cache_settings_statistics_album": "Sličice knjižnice", "cache_settings_statistics_full": "Izvirne slike", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Uporabi haptičen odziv", "haptic_feedback_title": "Haptičen odziv", "has_quota": "Ima kvoto", + "hash_asset": "Zgoščeno sredstvo", + "hashed_assets": "Zgoščena sredstva", + "hashing": "Zgoščevanje", "header_settings_add_header_tip": "Dodaj glavo", "header_settings_field_validator_msg": "Vrednost ne sme biti prazna", "header_settings_header_name_input": "Ime glave", @@ -1083,6 +1088,7 @@ "host": "Gostitelj", "hour": "Ura", "id": "ID", + "idle": "Nedejavnost", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naložene na strežnik Immich", "image": "Slika", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local": "Lokalno", "local_asset_cast_failed": "Sredstva, ki niso naložena na strežnik, ni mogoče predvajati", + "local_assets": "Lokalna sredstva", "local_network": "Lokalno omrežje", "local_network_sheet_info": "Aplikacija se bo povezala s strežnikom prek tega URL-ja, ko bo uporabljala navedeno omrežje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", @@ -1322,6 +1330,7 @@ "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj splošno ključno besedo", "no_shared_albums_message": "Ustvarite album za skupno rabo fotografij in videoposnetkov z osebami v vašem omrežju", + "no_uploads_in_progress": "Ni nalaganj v teku", "not_in_any_album": "Ni v nobenem albumu", "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -1359,6 +1368,7 @@ "original": "izvirnik", "other": "drugo", "other_devices": "Druge naprave", + "other_entities": "Drugi subjekti", "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", @@ -1390,7 +1400,7 @@ "pause": "Premor", "pause_memories": "Zaustavi spomine", "paused": "Zaustavljeno", - "pending": "V teku", + "pending": "Čakanje", "people": "Osebe", "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrščenih po osebah", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Osveževanje obrazev", "refreshing_metadata": "Osveževanje metapodatkov", "regenerating_thumbnails": "Obnavljanje sličic", + "remote": "Oddaljeno", + "remote_assets": "Oddaljena sredstva", "remove": "Odstrani", "remove_assets_album_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz albuma?", "remove_assets_shared_link_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz te skupne povezave?", @@ -1556,11 +1568,15 @@ "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", "reset_pin_code": "Ponastavi PIN kodo", + "reset_sqlite": "Ponastavi bazo podatkov SQLite", + "reset_sqlite_confirmation": "Ali ste prepričani, da želite ponastaviti bazo podatkov SQLite? Za ponovno sinhronizacijo podatkov se boste morali odjaviti in znova prijaviti", + "reset_sqlite_success": "Uspešno ponastavljena baza podatkov SQLite", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "Razreši dvojnike", "resolved_all_duplicates": "Razrešeni vsi dvojniki", "restore": "Obnovi", "restore_all": "Obnovi vse", + "restore_trash_action_prompt": "{count} obnovljenih iz koša", "restore_user": "Obnovi uporabnika", "restored_asset": "Obnovljeno sredstvo", "resume": "Nadaljuj", @@ -1569,6 +1585,7 @@ "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", + "running": "V teku", "save": "Shrani", "save_to_gallery": "Shrani v galerijo", "saved_api_key": "Shranjen API ključ", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "Predloži", + "success": "Uspeh", "suggestions": "Predlogi", "sunrise_on_the_beach": "Sončni vzhod na plaži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Sinhronizacija", "sync_albums": "Sinhronizacija albumov", "sync_albums_manual_subtitle": "Sinhronizirajte vse naložene videoposnetke in fotografije v izbrane varnostne albume", + "sync_local": "Sinhroniziraj lokalno", + "sync_remote": "Sinhroniziraj oddaljeno", "sync_upload_album_setting_subtitle": "Ustvarite in naložite svoje fotografije in videoposnetke v izbrane albume na Immich", "tag": "Oznaka", "tag_assets": "Označi sredstva", @@ -1841,6 +1861,7 @@ "tag_updated": "Posodobljena oznaka: {tag}", "tagged_assets": "Označeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "tags": "Oznake", + "tap_to_run_job": "Dotaknite se za zagon opravila", "template": "Predloga", "theme": "Tema", "theme_selection": "Izbira teme", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 28d6a5c0d5..bcbdcc5d6b 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -573,6 +573,8 @@ "backup_options_page_title": "备份选项", "backup_setting_subtitle": "管理后台和前台上传设置", "backward": "后退", + "beta_sync": "测试版同步状态", + "beta_sync_subtitle": "管理新的同步系统", "biometric_auth_enabled": "生物识别身份验证已启用", "biometric_locked_out": "您被锁定在生物识别身份验证之外", "biometric_no_options": "没有可用的生物识别选项", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "清除缓存", "cache_settings_clear_cache_button_title": "清除应用缓存。在重新生成缓存之前,将显著影响应用的性能。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "已加入黑名单的照片和视频", + "cache_settings_duplicated_assets_subtitle": "应用程序忽略的照片和视频", "cache_settings_duplicated_assets_title": "重复项目({count})", "cache_settings_statistics_album": "图库缩略图", "cache_settings_statistics_full": "完整图像", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "启用振动反馈", "haptic_feedback_title": "振动反馈", "has_quota": "配额大小", + "hash_asset": "哈希项目", + "hashed_assets": "已哈希的项目", + "hashing": "正在哈希", "header_settings_add_header_tip": "添加标头", "header_settings_field_validator_msg": "设置不可为空", "header_settings_header_name_input": "标头名称", @@ -1083,6 +1088,7 @@ "host": "服务器", "hour": "时", "id": "ID", + "idle": "空闲", "ignore_icloud_photos": "忽略 iCloud 照片", "ignore_icloud_photos_description": "存储在 iCloud 中的照片不会上传至 Immich 服务器", "image": "图片", @@ -1165,7 +1171,9 @@ "list": "列表", "loading": "加载中", "loading_search_results_failed": "加载搜索结果失败", + "local": "本地", "local_asset_cast_failed": "无法投放未上传至服务器的项目", + "local_assets": "本地项目", "local_network": "本地网络", "local_network_sheet_info": "当使用指定的 Wi-Fi 网络时,应用程序将通过此 URL 访问服务器", "location_permission": "定位权限", @@ -1322,6 +1330,7 @@ "no_results": "无结果", "no_results_description": "尝试使用同义词或更通用的关键词", "no_shared_albums_message": "创建相册以共享照片和视频", + "no_uploads_in_progress": "没有正在进行的上传", "not_in_any_album": "不在任何相册中", "not_selected": "未选择", "note_apply_storage_label_to_previously_uploaded assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -1359,6 +1368,7 @@ "original": "原图", "other": "其它", "other_devices": "其它设备", + "other_entities": "其他实体", "other_variables": "其它变量", "owned": "我的", "owner": "所有者", @@ -1519,6 +1529,8 @@ "refreshing_faces": "正在面部重新识别", "refreshing_metadata": "正在刷新元数据", "regenerating_thumbnails": "正在重新生成缩略图", + "remote": "远程", + "remote_assets": "远程项目", "remove": "移除", "remove_assets_album_confirmation": "确定要从图库中移除{count, plural, one {#个项目} other {#个项目}}?", "remove_assets_shared_link_confirmation": "确定要从共享链接中移除{count, plural, one {#个项目} other {#个项目}}?", @@ -1556,11 +1568,15 @@ "reset_password": "重置密码", "reset_people_visibility": "重置人物识别", "reset_pin_code": "重置PIN码", + "reset_sqlite": "重置 SQLite 数据库", + "reset_sqlite_confirmation": "您确定要重置 SQLite 数据库吗?您需要注销并重新登录才能重新同步数据", + "reset_sqlite_success": "已成功重置 SQLite 数据库", "reset_to_default": "恢复默认值", "resolve_duplicates": "处理重复项", "resolved_all_duplicates": "处理所有重复项", "restore": "恢复", "restore_all": "恢复全部", + "restore_trash_action_prompt": "从回收站中恢复了 {count} 项", "restore_user": "恢复用户", "restored_asset": "已恢复项目", "resume": "继续", @@ -1569,6 +1585,7 @@ "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", + "running": "正在运行", "save": "保存", "save_to_gallery": "保存到图库", "saved_api_key": "已保存的 API 密钥", @@ -1822,6 +1839,7 @@ "storage_quota": "存储配额", "storage_usage": "已用:{used}/{available}", "submit": "提交", + "success": "成功", "suggestions": "建议", "sunrise_on_the_beach": "海滩上的日出", "support": "支持", @@ -1831,6 +1849,8 @@ "sync": "同步", "sync_albums": "同步相册", "sync_albums_manual_subtitle": "将所有上传的视频和照片同步到选定的备份相册", + "sync_local": "同步本地", + "sync_remote": "同步远程", "sync_upload_album_setting_subtitle": "创建照片和视频并上传到 Immich 上的选定相册中", "tag": "标签", "tag_assets": "标记项目", @@ -1841,6 +1861,7 @@ "tag_updated": "已更新标签:{tag}", "tagged_assets": "{count, plural, one {# 个项目} other {# 个项目}}被加上标签", "tags": "标签", + "tap_to_run_job": "点击运行作业", "template": "模版", "theme": "主题", "theme_selection": "主题选项", From ab597155fa66664b582fe83bc558ecd1e78123a5 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 09:59:21 -0400 Subject: [PATCH 050/169] fix: immich-dev live reload (#20104) --- server/bin/immich-dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 533c10ef9d..e861c0ee06 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -6,4 +6,4 @@ if [ "$IMMICH_ENV" != "development" ]; then fi cd /usr/src/app/server || exit 1 -npm exec nest start --debug "0.0.0.0:9230" --watch -- "$@" +npm exec nest -- start --debug "0.0.0.0:9230" --watch -- "$@" From 92384c28de047c1b697c3f48343e137e7756a078 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 09:59:33 -0400 Subject: [PATCH 051/169] feat: sync auth user (#20067) --- e2e/test-assets | 2 +- mobile/openapi/README.md | 1 + mobile/openapi/lib/api.dart | 1 + mobile/openapi/lib/api_client.dart | 2 + .../openapi/lib/model/sync_auth_user_v1.dart | 215 ++++++++++++++++++ .../openapi/lib/model/sync_entity_type.dart | 3 + .../openapi/lib/model/sync_request_type.dart | 3 + mobile/openapi/lib/model/sync_user_v1.dart | 14 +- mobile/test/fixtures/sync_stream.stub.dart | 2 + open-api/immich-openapi-specs.json | 81 +++++++ open-api/typescript-sdk/src/fetch-client.ts | 2 + server/src/database.ts | 1 + server/src/dtos/sync.dto.ts | 18 ++ server/src/enum.ts | 3 + server/src/queries/sync.repository.sql | 24 ++ server/src/repositories/sync.repository.ts | 29 ++- server/src/services/sync.service.ts | 10 + server/test/medium.factory.ts | 7 + .../medium/specs/sync/sync-auth-user.spec.ts | 87 +++++++ .../test/medium/specs/sync/sync-user.spec.ts | 43 +--- 20 files changed, 507 insertions(+), 41 deletions(-) create mode 100644 mobile/openapi/lib/model/sync_auth_user_v1.dart create mode 100644 server/test/medium/specs/sync/sync-auth-user.spec.ts diff --git a/e2e/test-assets b/e2e/test-assets index 18736fc27a..37f60ea537 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 +Subproject commit 37f60ea537c0228f5f92e4f42dc42f0bb39a6d7f diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index b20a0694c5..2d764aa153 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -479,6 +479,7 @@ Class | Method | HTTP request | Description - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) + - [SyncAuthUserV1](doc//SyncAuthUserV1.md) - [SyncEntityType](doc//SyncEntityType.md) - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) - [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 545955a184..8b8acc0042 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -260,6 +260,7 @@ part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_face_delete_v1.dart'; part 'model/sync_asset_face_v1.dart'; part 'model/sync_asset_v1.dart'; +part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; part 'model/sync_memory_asset_v1.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 55d6f4108b..d9cae66dd3 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -576,6 +576,8 @@ class ApiClient { return SyncAssetFaceV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); + case 'SyncAuthUserV1': + return SyncAuthUserV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); case 'SyncMemoryAssetDeleteV1': diff --git a/mobile/openapi/lib/model/sync_auth_user_v1.dart b/mobile/openapi/lib/model/sync_auth_user_v1.dart new file mode 100644 index 0000000000..1dab7f47e3 --- /dev/null +++ b/mobile/openapi/lib/model/sync_auth_user_v1.dart @@ -0,0 +1,215 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAuthUserV1 { + /// Returns a new [SyncAuthUserV1] instance. + SyncAuthUserV1({ + required this.avatarColor, + required this.deletedAt, + required this.email, + required this.hasProfileImage, + required this.id, + required this.isAdmin, + required this.name, + required this.oauthId, + required this.pinCode, + required this.profileChangedAt, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + required this.storageLabel, + }); + + UserAvatarColor? avatarColor; + + DateTime? deletedAt; + + String email; + + bool hasProfileImage; + + String id; + + bool isAdmin; + + String name; + + String oauthId; + + String? pinCode; + + DateTime profileChangedAt; + + int? quotaSizeInBytes; + + int quotaUsageInBytes; + + String? storageLabel; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAuthUserV1 && + other.avatarColor == avatarColor && + other.deletedAt == deletedAt && + other.email == email && + other.hasProfileImage == hasProfileImage && + other.id == id && + other.isAdmin == isAdmin && + other.name == name && + other.oauthId == oauthId && + other.pinCode == pinCode && + other.profileChangedAt == profileChangedAt && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes && + other.storageLabel == storageLabel; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (email.hashCode) + + (hasProfileImage.hashCode) + + (id.hashCode) + + (isAdmin.hashCode) + + (name.hashCode) + + (oauthId.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + + (profileChangedAt.hashCode) + + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + + (quotaUsageInBytes.hashCode) + + (storageLabel == null ? 0 : storageLabel!.hashCode); + + @override + String toString() => 'SyncAuthUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, pinCode=$pinCode, profileChangedAt=$profileChangedAt, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, storageLabel=$storageLabel]'; + + Map toJson() { + final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; + json[r'id'] = this.id; + json[r'isAdmin'] = this.isAdmin; + json[r'name'] = this.name; + json[r'oauthId'] = this.oauthId; + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); + if (this.quotaSizeInBytes != null) { + json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; + } else { + // json[r'quotaSizeInBytes'] = null; + } + json[r'quotaUsageInBytes'] = this.quotaUsageInBytes; + if (this.storageLabel != null) { + json[r'storageLabel'] = this.storageLabel; + } else { + // json[r'storageLabel'] = null; + } + return json; + } + + /// Returns a new [SyncAuthUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAuthUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAuthUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAuthUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), + deletedAt: mapDateTime(json, r'deletedAt', r''), + email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, + id: mapValueOfType(json, r'id')!, + isAdmin: mapValueOfType(json, r'isAdmin')!, + name: mapValueOfType(json, r'name')!, + oauthId: mapValueOfType(json, r'oauthId')!, + pinCode: mapValueOfType(json, r'pinCode'), + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, + quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), + quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes')!, + storageLabel: mapValueOfType(json, r'storageLabel'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAuthUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAuthUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAuthUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAuthUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'avatarColor', + 'deletedAt', + 'email', + 'hasProfileImage', + 'id', + 'isAdmin', + 'name', + 'oauthId', + 'pinCode', + 'profileChangedAt', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'storageLabel', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 65ed78105c..5368126923 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -23,6 +23,7 @@ class SyncEntityType { String toJson() => value; + static const authUserV1 = SyncEntityType._(r'AuthUserV1'); static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); @@ -67,6 +68,7 @@ class SyncEntityType { /// List of all possible values in this [enum][SyncEntityType]. static const values = [ + authUserV1, userV1, userDeleteV1, assetV1, @@ -146,6 +148,7 @@ class SyncEntityTypeTypeTransformer { SyncEntityType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { + case r'AuthUserV1': return SyncEntityType.authUserV1; case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 800b3f4485..8a1857366e 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,7 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); static const partnersV1 = SyncRequestType._(r'PartnersV1'); @@ -51,6 +52,7 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + authUsersV1, memoriesV1, memoryToAssetsV1, partnersV1, @@ -107,6 +109,7 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; case r'PartnersV1': return SyncRequestType.partnersV1; diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index b9b41bb723..c01ddcc9fc 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -13,12 +13,15 @@ part of openapi.api; class SyncUserV1 { /// Returns a new [SyncUserV1] instance. SyncUserV1({ + required this.avatarColor, required this.deletedAt, required this.email, required this.id, required this.name, }); + UserAvatarColor? avatarColor; + DateTime? deletedAt; String email; @@ -29,6 +32,7 @@ class SyncUserV1 { @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && + other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && other.id == id && @@ -37,16 +41,22 @@ class SyncUserV1 { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + (id.hashCode) + (name.hashCode); @override - String toString() => 'SyncUserV1[deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.deletedAt != null) { json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); } else { @@ -67,6 +77,7 @@ class SyncUserV1 { final json = value.cast(); return SyncUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, id: mapValueOfType(json, r'id')!, @@ -118,6 +129,7 @@ class SyncUserV1 { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'avatarColor', 'deletedAt', 'email', 'id', diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index de2d58bc9d..6c47b9172e 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -9,6 +9,7 @@ abstract final class SyncStreamStub { email: "admin@admin", id: "1", name: "Admin", + avatarColor: null, ), ack: "1", ); @@ -19,6 +20,7 @@ abstract final class SyncStreamStub { email: "user@user", id: "5", name: "User", + avatarColor: null, ), ack: "5", ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index cd61f3e004..270a560fb0 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13978,8 +13978,79 @@ ], "type": "object" }, + "SyncAuthUserV1": { + "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "deletedAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "email": { + "type": "string" + }, + "hasProfileImage": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isAdmin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "oauthId": { + "type": "string" + }, + "pinCode": { + "nullable": true, + "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" + }, + "quotaSizeInBytes": { + "nullable": true, + "type": "integer" + }, + "quotaUsageInBytes": { + "type": "integer" + }, + "storageLabel": { + "nullable": true, + "type": "string" + } + }, + "required": [ + "avatarColor", + "deletedAt", + "email", + "hasProfileImage", + "id", + "isAdmin", + "name", + "oauthId", + "pinCode", + "profileChangedAt", + "quotaSizeInBytes", + "quotaUsageInBytes", + "storageLabel" + ], + "type": "object" + }, "SyncEntityType": { "enum": [ + "AuthUserV1", "UserV1", "UserDeleteV1", "AssetV1", @@ -14242,6 +14313,7 @@ "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "AuthUsersV1", "MemoriesV1", "MemoryToAssetsV1", "PartnersV1", @@ -14364,6 +14436,14 @@ }, "SyncUserV1": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "deletedAt": { "format": "date-time", "nullable": true, @@ -14380,6 +14460,7 @@ } }, "required": [ + "avatarColor", "deletedAt", "email", "id", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 81d279407c..bcbe757066 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -4099,6 +4099,7 @@ export enum Error2 { NotFound = "not_found" } export enum SyncEntityType { + AuthUserV1 = "AuthUserV1", UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", AssetV1 = "AssetV1", @@ -4149,6 +4150,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", PartnersV1 = "PartnersV1", diff --git a/server/src/database.ts b/server/src/database.ts index dc99fc5b31..53c39b7383 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -356,6 +356,7 @@ export const columns = { ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index c8b1a7dde9..8614bd5776 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -10,6 +10,7 @@ import { MemoryType, SyncEntityType, SyncRequestType, + UserAvatarColor, UserMetadataKey, } from 'src/enum'; import { UserMetadata } from 'src/types'; @@ -58,9 +59,25 @@ export class SyncUserV1 { id!: string; name!: string; email!: string; + @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) + avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; } +@ExtraModel() +export class SyncAuthUserV1 extends SyncUserV1 { + isAdmin!: boolean; + pinCode!: string | null; + oauthId!: string; + storageLabel!: string | null; + @ApiProperty({ type: 'integer' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer' }) + quotaUsageInBytes!: number; + hasProfileImage!: boolean; + profileChangedAt!: Date; +} + @ExtraModel() export class SyncUserDeleteV1 { userId!: string; @@ -301,6 +318,7 @@ export class SyncAckV1 {} export class SyncResetV1 {} export type SyncItem = { + [SyncEntityType.AuthUserV1]: SyncAuthUserV1; [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; [SyncEntityType.PartnerV1]: SyncPartnerV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index f2eae615ab..75a96dfe67 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -559,6 +559,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', PartnersV1 = 'PartnersV1', @@ -573,6 +574,8 @@ export enum SyncRequestType { } export enum SyncEntityType { + AuthUserV1 = 'AuthUserV1', + UserV1 = 'UserV1', UserDeleteV1 = 'UserDeleteV1', diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7502b79f57..7c7774a020 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -444,6 +444,29 @@ where order by "asset_face"."updateId" asc +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes", + "profileImagePath", + "profileChangedAt" +from + "user" +where + "updatedAt" < now() - interval '1 millisecond' +order by + "updateId" asc + -- SyncRepository.memory.getDeletes select "id", @@ -871,6 +894,7 @@ select "id", "name", "email", + "avatarColor", "deletedAt", "updateId" from diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index dba52d25a0..486984118d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -43,6 +43,7 @@ export class SyncRepository { asset: AssetSync; assetExif: AssetExifSync; assetFace: AssetFaceSync; + authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; @@ -63,6 +64,7 @@ export class SyncRepository { this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); this.assetFace = new AssetFaceSync(this.db); + this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); @@ -367,6 +369,27 @@ class AssetSync extends BaseSync { } } +class AuthUserSync extends BaseSync { + @GenerateSql({ params: [], stream: true }) + getUpserts(ack?: SyncAck) { + return this.db + .selectFrom('user') + .select(columns.syncUser) + .select([ + 'isAdmin', + 'pinCode', + 'oauthId', + 'storageLabel', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'profileImagePath', + 'profileChangedAt', + ]) + .$call(this.upsertTableFilters(ack)) + .stream(); + } +} + class PersonSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getDeletes(userId: string, ack?: SyncAck) { @@ -693,11 +716,7 @@ class UserSync extends BaseSync { @GenerateSql({ params: [], stream: true }) getUpserts(ack?: SyncAck) { - return this.db - .selectFrom('user') - .select(['id', 'name', 'email', 'deletedAt', 'updateId']) - .$call(this.upsertTableFilters(ack)) - .stream(); + return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(ack)).stream(); } } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index fb582ab038..57b953f12e 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -54,6 +54,7 @@ const sendEntityBackfillCompleteAck = (response: Writable, ackType: SyncEntityTy const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ + SyncRequestType.AuthUsersV1, SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, @@ -140,6 +141,7 @@ export class SyncService extends BaseService { const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); const handlers: Record Promise> = { + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(response, checkpointMap), [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), @@ -169,6 +171,14 @@ export class SyncService extends BaseService { response.end(); } + private async syncAuthUsersV1(response: Writable, checkpointMap: CheckpointMap) { + const upsertType = SyncEntityType.AuthUserV1; + const upserts = this.syncRepository.authUser.getUpserts(checkpointMap[upsertType]); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); + } + } + private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d6038b6b84..1b669e83e4 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -507,7 +507,14 @@ const userInsert = (user: Partial> = {}) => { deletedAt: null, isAdmin: false, profileImagePath: '', + profileChangedAt: newDate(), shouldChangePassword: true, + storageLabel: null, + pinCode: null, + oauthId: '', + avatarColor: null, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, }; return { ...defaults, ...user, id }; diff --git a/server/test/medium/specs/sync/sync-auth-user.spec.ts b/server/test/medium/specs/sync/sync-auth-user.spec.ts new file mode 100644 index 0000000000..80ce8b37fa --- /dev/null +++ b/server/test/medium/specs/sync/sync-auth-user.spec.ts @@ -0,0 +1,87 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AuthUserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: user.id, + isAdmin: user.isAdmin, + deletedAt: user.deletedAt, + name: user.name, + avatarColor: user.avatarColor, + email: user.email, + pinCode: user.pinCode, + hasProfileImage: false, + profileChangedAt: (user.profileChangedAt as Date).toISOString(), + oauthId: user.oauthId, + quotaSizeInBytes: user.quotaSizeInBytes, + quotaUsageInBytes: user.quotaUsageInBytes, + storageLabel: user.storageLabel, + }, + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AuthUsersV1])).resolves.toEqual([]); + }); + + it('should sync a change and then another change to that same user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const userRepo = ctx.get(UserRepository); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await userRepo.update(user.id, { isAdmin: true }); + + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(newResponse).toHaveLength(1); + expect(newResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: true, + }), + type: 'AuthUserV1', + }, + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 24137e3aea..72661e119c 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -37,6 +37,7 @@ describe(SyncEntityType.UserV1, () => { email: user.email, id: user.id, name: user.name, + avatarColor: user.avatarColor, }, type: 'UserV1', }, @@ -49,8 +50,7 @@ describe(SyncEntityType.UserV1, () => { it('should detect and sync a soft deleted user', async () => { const { auth, ctx } = await setup(await getKyselyDB()); - const deletedAt = new Date().toISOString(); - const { user: deleted } = await ctx.newUser({ deletedAt }); + const { user: deleted } = await ctx.newUser({ deletedAt: new Date().toISOString() }); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); @@ -59,22 +59,12 @@ describe(SyncEntityType.UserV1, () => { expect.arrayContaining([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: auth.user.id }), type: 'UserV1', }, { ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, + data: expect.objectContaining({ id: deleted.id }), type: 'UserV1', }, ]), @@ -85,7 +75,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should detect and sync a deleted user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user: authUser, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -104,12 +94,7 @@ describe(SyncEntityType.UserV1, () => { }, { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: authUser.id }), type: 'UserV1', }, ]); @@ -119,7 +104,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should sync a user and then an update to that same user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -128,12 +113,7 @@ describe(SyncEntityType.UserV1, () => { expect(response).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: user.id }), type: 'UserV1', }, ]); @@ -147,12 +127,7 @@ describe(SyncEntityType.UserV1, () => { expect(newResponse).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, + data: expect.objectContaining({ id: user.id, name: updated.name }), type: 'UserV1', }, ]); From 08122d687128e9b5b60baf6d861fa4683bdf0b0d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:43:15 +0530 Subject: [PATCH 052/169] fix: show only local assets from albums selected for backup (#20050) * show only local assets from albums selected for backup # Conflicts: # mobile/lib/infrastructure/repositories/db.repository.drift.dart * ignore backup selection * fix: backup album ownerId * fix: backup album ownerId * only show local only assets that are selected for backup * add index on visibility and backup selection * fix: video not playing in search view * remove safe area from bottom bar * refactor stack count with a CTE and local asset with a SELECT * fix lint * remove redundant COALESCE * remove stack count from main timeline query --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v4.json | 2 +- .../models/asset/remote_asset.model.dart | 11 +- .../entities/merged_asset.drift | 143 +- .../entities/merged_asset.drift.dart | 47 +- .../repositories/backup.repository.dart | 11 +- .../repositories/db.repository.dart | 4 +- .../repositories/db.repository.drift.dart | 98 +- .../repositories/db.repository.steps.dart | 84 +- .../repositories/remote_asset.repository.dart | 30 +- .../repositories/timeline.repository.dart | 3 +- .../lib/pages/backup/drift_backup.page.dart | 27 +- .../drift_backup_album_selection.page.dart | 18 +- mobile/lib/pages/common/tab_shell.page.dart | 10 +- .../asset_viewer/asset_stack.provider.dart | 6 +- .../widgets/images/thumbnail_tile.widget.dart | 40 +- .../providers/app_life_cycle.provider.dart | 10 +- .../backup/drift_backup.provider.dart | 14 +- mobile/lib/services/drift_backup.service.dart | 11 +- .../test/drift/main/generated/schema_v4.dart | 1282 ++++++++--------- 19 files changed, 916 insertions(+), 935 deletions(-) diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json index 82ef30adae..2488319a5e 100644 --- a/mobile/drift_schemas/main/drift_schema_v4.json +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 760a16170b..f96fa18a74 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -15,7 +15,6 @@ class RemoteAsset extends BaseAsset { final AssetVisibility visibility; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -34,7 +33,6 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override @@ -61,7 +59,6 @@ class RemoteAsset extends BaseAsset { thumbHash: ${thumbHash ?? ""}, visibility: $visibility, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -77,8 +74,7 @@ class RemoteAsset extends BaseAsset { ownerId == other.ownerId && thumbHash == other.thumbHash && visibility == other.visibility && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -89,8 +85,7 @@ class RemoteAsset extends BaseAsset { localId.hashCode ^ thumbHash.hashCode ^ visibility.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -109,7 +104,6 @@ class RemoteAsset extends BaseAsset { AssetVisibility? visibility, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -128,7 +122,6 @@ class RemoteAsset extends BaseAsset { visibility: visibility ?? this.visibility, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c15..9778ba723b 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,68 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in ? + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) ORDER BY created_at DESC LIMIT $limit; @@ -85,17 +77,14 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 + AND rae.visibility = 0 -- timeline visibility AND rae.owner_id in ? AND ( rae.stack_id IS NULL @@ -103,14 +92,18 @@ FROM ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae LEFT JOIN remote_asset_entity rae ON rae.checksum = lae.checksum + LEFT JOIN + local_album_asset_entity laa ON laa.asset_id = lae.id + LEFT JOIN + local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1..7f56b25d4e 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -3,24 +3,29 @@ import 'package:drift/drift.dart' as i0; import 'package:drift/internal/modular.dart' as i1; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i4; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i7; class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); i0.Selectable mergedAsset(List var1, - {required i0.Limit limit}) { + {required MergedAsset$limit limit}) { var $arrayStartIndex = 1; final expandedvar1 = $expandVar($arrayStartIndex, var1.length); $arrayStartIndex += var1.length; - final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + final generatedlimit = $write(limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ for (var $ in var1) i0.Variable($), ...generatedlimit.introducedVariables @@ -29,12 +34,14 @@ class MergedAssetDrift extends i1.ModularAccessor { remoteAssetEntity, localAssetEntity, stackEntity, + localAlbumAssetEntity, + localAlbumEntity, ...generatedlimit.watchedTables, }).map((i0.QueryRow row) => MergedAssetResult( remoteId: row.readNullable('remote_id'), localId: row.readNullable('local_id'), name: row.read('name'), - type: i3.$RemoteAssetEntityTable.$convertertype + type: i4.$RemoteAssetEntityTable.$convertertype .fromSql(row.read('type')), createdAt: row.read('created_at'), updatedAt: row.read('updated_at'), @@ -48,7 +55,6 @@ class MergedAssetDrift extends i1.ModularAccessor { livePhotoVideoId: row.readNullable('live_photo_video_id'), orientation: row.read('orientation'), stackId: row.readNullable('stack_id'), - stackCount: row.read('stack_count'), )); } @@ -58,30 +64,39 @@ class MergedAssetDrift extends i1.ModularAccessor { final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), for (var $ in var2) i0.Variable($) ], readsFrom: { remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, }).map((i0.QueryRow row) => MergedBucketResult( assetCount: row.read('asset_count'), bucketDate: row.read('bucket_date'), )); } - i3.$RemoteAssetEntityTable get remoteAssetEntity => + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i4.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); + .resultSet('remote_asset_entity'); i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer(attachedDatabase) .resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_asset_entity'); + i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet( + 'local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_album_entity'); } class MergedAssetResult { @@ -101,7 +116,6 @@ class MergedAssetResult { final String? livePhotoVideoId; final int orientation; final String? stackId; - final int stackCount; MergedAssetResult({ this.remoteId, this.localId, @@ -119,10 +133,11 @@ class MergedAssetResult { this.livePhotoVideoId, required this.orientation, this.stackId, - required this.stackCount, }); } +typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); + class MergedBucketResult { final int assetCount; final String bucketDate; diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 99df206db5..aed7118e5a 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -50,7 +50,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getRemainderCount() async { + Future getRemainderCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ @@ -74,7 +74,8 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..where( _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNull() & + (_db.remoteAssetEntity.id.isNull() | + _db.remoteAssetEntity.ownerId.equals(userId).not()) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -82,7 +83,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getBackupCount() async { + Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns( [_db.localAlbumAssetEntity.assetId], @@ -109,6 +110,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & _db.remoteAssetEntity.id.isNotNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -116,7 +118,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future> getCandidates() async { + Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) ..where( @@ -141,6 +143,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..addColumns([_db.remoteAssetEntity.checksum]) ..where( _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId) & lae.checksum.isNotNull(), ), ) & diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index ced148f855..5cbbb3b4f4 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,8 +4,8 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -97,7 +97,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.alterTable(TableMigration(v3.stackEntity)); }, from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added await m.create(v4.assetFaceEntity); }, ), diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 7b722dfff6..f5962f09ab 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -5,17 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i4; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i6; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i7; + as i5; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i7; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' as i8; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i9; @@ -43,17 +43,17 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2.$RemoteAssetEntityTable(this); - late final i3.$LocalAssetEntityTable localAssetEntity = - i3.$LocalAssetEntityTable(this); - late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this); - late final i5.$UserMetadataEntityTable userMetadataEntity = - i5.$UserMetadataEntityTable(this); - late final i6.$PartnerEntityTable partnerEntity = - i6.$PartnerEntityTable(this); - late final i7.$LocalAlbumEntityTable localAlbumEntity = - i7.$LocalAlbumEntityTable(this); - late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i8.$LocalAlbumAssetEntityTable(this); + late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = + i4.$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = + i5.$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = + i6.$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = + i7.$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = + i8.$PartnerEntityTable(this); late final i9.$RemoteExifEntityTable remoteExifEntity = i9.$RemoteExifEntityTable(this); late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = @@ -77,15 +77,15 @@ abstract class $Drift extends i0.GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, - i3.idxLocalAssetChecksum, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, i2.uQRemoteAssetOwnerChecksum, i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -113,6 +113,22 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_album_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('user_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -135,22 +151,6 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), ], ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('remote_asset_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -260,18 +260,18 @@ class $DriftManager { i1.$$UserEntityTableTableManager(_db, _db.userEntity); i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); - i3.$$LocalAssetEntityTableTableManager get localAssetEntity => - i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i4.$$StackEntityTableTableManager get stackEntity => - i4.$$StackEntityTableTableManager(_db, _db.stackEntity); - i5.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i6.$$PartnerEntityTableTableManager get partnerEntity => - i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8 + i3.$$StackEntityTableTableManager get stackEntity => + i3.$$StackEntityTableTableManager(_db, _db.stackEntity); + i4.$$LocalAssetEntityTableTableManager get localAssetEntity => + i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i5.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); + i7.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i8.$$PartnerEntityTableTableManager get partnerEntity => + i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); i9.$$RemoteExifEntityTableTableManager get remoteExifEntity => i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index d8c35707ed..7f179fb5f0 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1273,15 +1273,15 @@ final class Schema4 extends i0.VersionedSchema { late final List entities = [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -1342,6 +1342,24 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); late final Shape2 localAssetEntity = Shape2( source: i0.VersionedTable( entityName: 'local_asset_entity', @@ -1366,9 +1384,9 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape3 stackEntity = Shape3( + late final Shape6 localAlbumEntity = Shape6( source: i0.VersionedTable( - entityName: 'stack_entity', + entityName: 'local_album_entity', withoutRowId: true, isStrict: true, tableConstraints: [ @@ -1376,10 +1394,26 @@ final class Schema4 extends i0.VersionedSchema { ], columns: [ _column_0, - _column_9, + _column_1, _column_5, - _column_15, - _column_75, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, ], attachedDatabase: database, ), @@ -1423,40 +1457,6 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); - late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); late final Shape8 remoteExifEntity = Shape8( source: i0.VersionedTable( entityName: 'remote_exif_entity', diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 64c602a2c7..c3b15efb83 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -32,49 +32,21 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } Stream watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - final query = _db.remoteAssetEntity.select().addColumns([ _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, ]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; - - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 7bbae9a80a..fe9ae1e60d 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -71,7 +71,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required int count, }) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +90,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 7780413399..08b09250e1 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; @@ -24,12 +25,24 @@ class _DriftBackupPageState extends ConsumerState { @override void initState() { super.initState(); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); } Future startBackup() async { - await ref.read(driftBackupProvider.notifier).getBackupStatus(); - await ref.read(driftBackupProvider.notifier).backup(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); + await ref.read(driftBackupProvider.notifier).backup(currentUser.id); } Future stopBackup() async { @@ -207,7 +220,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { trailing: ElevatedButton( onPressed: () async { await context.pushRoute(const DriftBackupAlbumSelectionRoute()); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); }, child: const Text( "select", diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 18d3ee1156..f0f3bedaab 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -4,17 +4,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -92,7 +92,15 @@ class _DriftBackupAlbumSelectionPageState if (didPop && !_hasPopped) { _hasPopped = true; - await ref.read(driftBackupProvider.notifier).getBackupStatus(); + super.initState(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); @@ -107,7 +115,7 @@ class _DriftBackupAlbumSelectionPageState final backupNotifier = ref.read(driftBackupProvider.notifier); backupNotifier.cancel().then((_) { - backupNotifier.backup(); + backupNotifier.backup(currentUser.id); }); } } diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index b0be136a15..418c8b9e39 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -38,7 +39,14 @@ class _TabShellPageState extends ConsumerState { await runNewSync(ref, full: true).then((_) async { if (isEnableBackup) { - await ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); }); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56c..5b86258e72 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -6,11 +6,7 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { @override Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { + if (asset == null || asset is! RemoteAsset || asset.stackId == null) { return const []; } diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629f..b059f5feaa 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -54,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget { : const BoxDecoration(); final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + asset is RemoteAsset && (asset as RemoteAsset).stackId != null; return Stack( children: [ @@ -86,9 +86,7 @@ class ThumbnailTile extends ConsumerWidget { right: 10.0, top: asset.isVideo ? 24.0 : 6.0, ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), + child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), if (asset.isVideo) @@ -198,40 +196,6 @@ class _SelectionIndicator extends StatelessWidget { } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3be46d2fbd..647ada4a64 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; @@ -110,7 +111,14 @@ class AppLifeCycleNotifier extends StateNotifier { .getSetting(AppSettingsEnum.enableBackup); if (isEnableBackup) { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); } catch (e, stackTrace) { diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 3a2c7fd9ce..45941639a0 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -329,11 +329,11 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future getBackupStatus() async { + Future getBackupStatus(String userId) async { final [totalCount, backupCount, remainderCount] = await Future.wait([ _backupService.getTotalCount(), - _backupService.getBackupCount(), - _backupService.getRemainderCount(), + _backupService.getBackupCount(userId), + _backupService.getRemainderCount(userId), ]); state = state.copyWith( @@ -343,8 +343,8 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future backup() { - return _backupService.backup(_updateEnqueueCount); + Future backup(String userId) { + return _backupService.backup(userId, _updateEnqueueCount); } void _updateEnqueueCount(EnqueueStatus status) { @@ -379,11 +379,11 @@ class ExpBackupNotifier extends StateNotifier { } } - Future handleBackupResume() async { + Future handleBackupResume(String userId) async { final tasks = await FileDownloader().allTasks(group: kBackupGroup); if (tasks.isEmpty) { // Start a new backup queue - await backup(); + await backup(userId); } debugPrint("Tasks to resume: ${tasks.length}"); diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 2f51c261fb..7373643c95 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -48,20 +48,21 @@ class DriftBackupService { return _backupRepository.getTotalCount(); } - Future getRemainderCount() { - return _backupRepository.getRemainderCount(); + Future getRemainderCount(String userId) { + return _backupRepository.getRemainderCount(userId); } - Future getBackupCount() { - return _backupRepository.getBackupCount(); + Future getBackupCount(String userId) { + return _backupRepository.getBackupCount(userId); } Future backup( + String userId, void Function(EnqueueStatus status) onEnqueueTasks, ) async { shouldCancel = false; - final candidates = await _backupRepository.getCandidates(); + final candidates = await _backupRepository.getCandidates(userId); if (candidates.isEmpty) { return; } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index d02e2ff9c4..9cf72a098b 100644 --- a/mobile/test/drift/main/generated/schema_v4.dart +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -982,6 +982,255 @@ class RemoteAssetEntityCompanion } } +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + class LocalAssetEntity extends Table with TableInfo { @override @@ -1415,640 +1664,6 @@ class LocalAssetEntityCompanion extends UpdateCompanion { } } -class StackEntity extends Table with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'stack_entity'; - @override - Set get $primaryKey => {id}; - @override - StackEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, - ); - } - - @override - StackEntity createAlias(String alias) { - return StackEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class StackEntityData extends DataClass implements Insertable { - final String id; - final DateTime createdAt; - final DateTime updatedAt; - final String ownerId; - final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - map['owner_id'] = Variable(ownerId); - map['primary_asset_id'] = Variable(primaryAssetId); - return map; - } - - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StackEntityData( - id: serializer.fromJson(json['id']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - ownerId: serializer.fromJson(json['ownerId']), - primaryAssetId: serializer.fromJson(json['primaryAssetId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'ownerId': serializer.toJson(ownerId), - 'primaryAssetId': serializer.toJson(primaryAssetId), - }; - } - - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - StackEntityData copyWithCompanion(StackEntityCompanion data) { - return StackEntityData( - id: data.id.present ? data.id.value : this.id, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present - ? data.primaryAssetId.value - : this.primaryAssetId, - ); - } - - @override - String toString() { - return (StringBuffer('StackEntityData(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StackEntityData && - other.id == this.id && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.ownerId == this.ownerId && - other.primaryAssetId == this.primaryAssetId); -} - -class StackEntityCompanion extends UpdateCompanion { - final Value id; - final Value createdAt; - final Value updatedAt; - final Value ownerId; - final Value primaryAssetId; - const StackEntityCompanion({ - this.id = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.ownerId = const Value.absent(), - this.primaryAssetId = const Value.absent(), - }); - StackEntityCompanion.insert({ - required String id, - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - required String ownerId, - required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); - static Insertable custom({ - Expression? id, - Expression? createdAt, - Expression? updatedAt, - Expression? ownerId, - Expression? primaryAssetId, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (ownerId != null) 'owner_id': ownerId, - if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, - }); - } - - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { - return StackEntityCompanion( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (ownerId.present) { - map['owner_id'] = Variable(ownerId.value); - } - if (primaryAssetId.present) { - map['primary_asset_id'] = Variable(primaryAssetId.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StackEntityCompanion(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } -} - -class UserMetadataEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); - @override - List get $columns => [userId, key, value]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'user_metadata_entity'; - @override - Set get $primaryKey => {userId, key}; - @override - UserMetadataEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, - ); - } - - @override - UserMetadataEntity createAlias(String alias) { - return UserMetadataEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class UserMetadataEntityData extends DataClass - implements Insertable { - final String userId; - final int key; - final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['key'] = Variable(key); - map['value'] = Variable(value); - return map; - } - - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return UserMetadataEntityData( - userId: serializer.fromJson(json['userId']), - key: serializer.fromJson(json['key']), - value: serializer.fromJson(json['value']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'key': serializer.toJson(key), - 'value': serializer.toJson(value), - }; - } - - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { - return UserMetadataEntityData( - userId: data.userId.present ? data.userId.value : this.userId, - key: data.key.present ? data.key.value : this.key, - value: data.value.present ? data.value.value : this.value, - ); - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityData(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is UserMetadataEntityData && - other.userId == this.userId && - other.key == this.key && - $driftBlobEquality.equals(other.value, this.value)); -} - -class UserMetadataEntityCompanion - extends UpdateCompanion { - final Value userId; - final Value key; - final Value value; - const UserMetadataEntityCompanion({ - this.userId = const Value.absent(), - this.key = const Value.absent(), - this.value = const Value.absent(), - }); - UserMetadataEntityCompanion.insert({ - required String userId, - required int key, - required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); - static Insertable custom({ - Expression? userId, - Expression? key, - Expression? value, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (key != null) 'key': key, - if (value != null) 'value': value, - }); - } - - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { - return UserMetadataEntityCompanion( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (key.present) { - map['key'] = Variable(key.value); - } - if (value.present) { - map['value'] = Variable(value.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityCompanion(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } -} - -class PartnerEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - @override - List get $columns => [sharedById, sharedWithId, inTimeline]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'partner_entity'; - @override - Set get $primaryKey => {sharedById, sharedWithId}; - @override - PartnerEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, - ); - } - - @override - PartnerEntity createAlias(String alias) { - return PartnerEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class PartnerEntityData extends DataClass - implements Insertable { - final String sharedById; - final String sharedWithId; - final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['shared_by_id'] = Variable(sharedById); - map['shared_with_id'] = Variable(sharedWithId); - map['in_timeline'] = Variable(inTimeline); - return map; - } - - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PartnerEntityData( - sharedById: serializer.fromJson(json['sharedById']), - sharedWithId: serializer.fromJson(json['sharedWithId']), - inTimeline: serializer.fromJson(json['inTimeline']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'sharedById': serializer.toJson(sharedById), - 'sharedWithId': serializer.toJson(sharedWithId), - 'inTimeline': serializer.toJson(inTimeline), - }; - } - - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { - return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present - ? data.sharedWithId.value - : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, - ); - } - - @override - String toString() { - return (StringBuffer('PartnerEntityData(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PartnerEntityData && - other.sharedById == this.sharedById && - other.sharedWithId == this.sharedWithId && - other.inTimeline == this.inTimeline); -} - -class PartnerEntityCompanion extends UpdateCompanion { - final Value sharedById; - final Value sharedWithId; - final Value inTimeline; - const PartnerEntityCompanion({ - this.sharedById = const Value.absent(), - this.sharedWithId = const Value.absent(), - this.inTimeline = const Value.absent(), - }); - PartnerEntityCompanion.insert({ - required String sharedById, - required String sharedWithId, - this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); - static Insertable custom({ - Expression? sharedById, - Expression? sharedWithId, - Expression? inTimeline, - }) { - return RawValuesInsertable({ - if (sharedById != null) 'shared_by_id': sharedById, - if (sharedWithId != null) 'shared_with_id': sharedWithId, - if (inTimeline != null) 'in_timeline': inTimeline, - }); - } - - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { - return PartnerEntityCompanion( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (sharedById.present) { - map['shared_by_id'] = Variable(sharedById.value); - } - if (sharedWithId.present) { - map['shared_with_id'] = Variable(sharedWithId.value); - } - if (inTimeline.present) { - map['in_timeline'] = Variable(inTimeline.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PartnerEntityCompanion(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } -} - class LocalAlbumEntity extends Table with TableInfo { @override @@ -2498,6 +2113,391 @@ class LocalAlbumAssetEntityCompanion } } +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + class RemoteExifEntity extends Table with TableInfo { @override @@ -5467,8 +5467,11 @@ class DatabaseAtV4 extends GeneratedDatabase { DatabaseAtV4(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); - late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); late final Index uQRemoteAssetOwnerChecksum = Index( @@ -5478,9 +5481,6 @@ class DatabaseAtV4 extends GeneratedDatabase { 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); - late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = - LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = @@ -5498,15 +5498,15 @@ class DatabaseAtV4 extends GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, From 1a35a01149fda1d33470905203e3524c6db944cd Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Jul 2025 09:20:52 -0500 Subject: [PATCH 053/169] feat: drift manual upload (#20101) --- i18n/en.json | 3 ++ .../repositories/timeline.repository.dart | 5 +++ mobile/lib/main.dart | 13 +++++++ .../upload_action_button.widget.dart | 36 ++++++++++++++++++- .../asset_viewer/bottom_bar.widget.dart | 3 ++ .../asset_viewer/bottom_sheet.widget.dart | 2 +- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 2 +- .../local_album_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../infrastructure/action.provider.dart | 18 ++++++++++ .../lib/repositories/upload.repository.dart | 5 +++ mobile/lib/services/drift_backup.service.dart | 28 +++++++++++++-- 14 files changed, 114 insertions(+), 9 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 54c7ca6f1b..e2e0fd3c4f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1941,11 +1941,13 @@ "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", + "upload_action_prompt": "{count} queued for upload", "upload_concurrency": "Upload concurrency", "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", + "upload_finished": "Upload finished", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicates", @@ -1954,6 +1956,7 @@ "upload_success": "Upload success, refresh the page to see new upload assets.", "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", + "uploading_media": "Uploading media", "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index fe9ae1e60d..2916993a9b 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -141,6 +141,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), useColumns: false, ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), ]) ..addColumns([assetCountExp, dateExp]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9f39a89b33..b62fb180e5 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -203,6 +203,19 @@ class ImmichAppState extends ConsumerState ), progressBar: true, ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification( + 'uploading_media'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + complete: TaskNotification( + 'upload_finished'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + progressBar: true, + ); } Future _deepLinkBuilder(PlatformDeepLink deepLink) async { diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 66467e44a3..d67c952b1a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -1,16 +1,50 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class UploadActionButton extends ConsumerWidget { - const UploadActionButton({super.key}); + final ActionSource source; + + const UploadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).upload(source); + + final successMessage = 'upload_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + + ref.read(multiSelectProvider.notifier).reset(); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.backup_outlined, label: "upload".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index cb558804d2..65bab5acb8 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -37,6 +38,8 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), + if (asset.isLocalOnly) + const UploadActionButton(source: ActionSource.viewer), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), ]; diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 0e426fde6d..9111469629 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -63,7 +63,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ]; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index deaaea0d39..76243cf803 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class ArchiveBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index 8199271bfe..3f3f933745 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class FavoriteBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index d83b8e399d..c148861b43 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart index 2ad0fe7485..b1e87dfaea 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { actions: [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), - UploadActionButton(), + UploadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 2e6047f0ba..ff77c79906 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], RemoveFromAlbumActionButton( source: ActionSource.timeline, diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 419ba0f902..3102511295 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asse import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/drift_backup.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -32,12 +33,14 @@ class ActionResult { class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; + late DriftBackupService _backupService; ActionNotifier() : super(); @override void build() { _service = ref.watch(actionServiceProvider); + _backupService = ref.watch(driftBackupServiceProvider); } List _getRemoteIdsForSource(ActionSource source) { @@ -368,6 +371,21 @@ class ActionNotifier extends Notifier { ); } } + + Future upload(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(); + try { + await _backupService.manualBackup(assets); + return ActionResult(count: assets.length, success: true); + } catch (error, stack) { + _logger.severe('Failed manually upload assets', error, stack); + return ActionResult( + count: assets.length, + success: false, + error: error.toString(), + ); + } + } } extension on Iterable { diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index b98eece656..6d75313a24 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -20,6 +20,11 @@ class UploadRepository { taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); + FileDownloader().registerCallbacks( + group: kManualUploadGroup, + taskStatusCallback: (_) => {}, + taskProgressCallback: (_) => {}, + ); } void enqueueAll(List tasks) { diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 7373643c95..ab878969a0 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -26,6 +26,7 @@ final driftBackupServiceProvider = Provider( ), ); +// TODO: Rename to UploadService after removing Isar class DriftBackupService { DriftBackupService( this._backupRepository, @@ -56,6 +57,24 @@ class DriftBackupService { return _backupRepository.getBackupCount(userId); } + Future manualBackup(List localAssets) async { + List tasks = []; + for (final asset in localAssets) { + final task = await _getUploadTask( + asset, + group: kManualUploadGroup, + priority: 1, // High priority after upload motion photo part + ); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty) { + _uploadService.enqueueTasks(tasks); + } + } + Future backup( String userId, void Function(EnqueueStatus status) onEnqueueTasks, @@ -147,7 +166,11 @@ class DriftBackupService { } } - Future _getUploadTask(LocalAsset asset) async { + Future _getUploadTask( + LocalAsset asset, { + String group = kBackupGroup, + int? priority, + }) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -193,7 +216,8 @@ class DriftBackupService { originalFileName: originalFileName, deviceAssetId: asset.id, metadata: metadata, - group: kBackupGroup, + group: group, + priority: priority, ); } From 0174de19dd712aa647289b0b9c4c161cae3a6628 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Jul 2025 09:40:16 -0500 Subject: [PATCH 054/169] feat: export database option (#20098) --- i18n/en.json | 2 + .../beta_sync_settings.dart | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index e2e0fd3c4f..73338ff6cc 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1002,6 +1002,8 @@ "explorer": "Explorer", "export": "Export", "export_as_json": "Export as JSON", + "export_database": "Export Database", + "export_database_description": "Export the SQLite database", "extension": "Extension", "external": "External", "external_libraries": "External Libraries", diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 3d37fb102b..e69717d3a4 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:drift/drift.dart' as drift_db; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -10,6 +12,9 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; class BetaSyncSettings extends HookConsumerWidget { const BetaSyncSettings({ @@ -69,6 +74,67 @@ class BetaSyncSettings extends HookConsumerWidget { }); } + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref + .read(driftProvider) + .customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Database file not found".t(context: context)), + ), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File( + path.join( + documentsDir.path, + 'immich_export_$timestamp.sqlite', + ), + ); + + await dbFile.copy(exportFile.path); + + await Share.shareXFiles( + [XFile(exportFile.path)], + text: 'Immich Database Export', + ); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Database exported successfully".t(context: context)), + ), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Failed to export database: $e".t(context: context)), + ), + ); + } + } + } + return FutureBuilder>( future: loadCounts(), builder: (context, snapshot) { @@ -232,6 +298,19 @@ class BetaSyncSettings extends HookConsumerWidget { ), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "export_database_description".t(context: context), + ), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), ListTile( title: Text( "reset_sqlite".t(context: context), From 0e1c8c2b8086bd2fc6a7433431e9c18c598eb6de Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:12:06 +0200 Subject: [PATCH 055/169] fix: ML recognition distance UI form validation (#20107) --- .../machine-learning-settings.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte index c120cb3750..acc920981b 100644 --- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte +++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte @@ -183,7 +183,7 @@ label={$t('admin.machine_learning_min_detection_score')} description={$t('admin.machine_learning_min_detection_score_description')} bind:value={config.machineLearning.facialRecognition.minScore} - step="0.1" + step="0.01" min={0.1} max={1} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} @@ -196,7 +196,7 @@ label={$t('admin.machine_learning_max_recognition_distance')} description={$t('admin.machine_learning_max_recognition_distance_description')} bind:value={config.machineLearning.facialRecognition.maxDistance} - step="0.1" + step="0.01" min={0.1} max={2} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} From 2bead445bdb6c9717216e61ba8e1f7370e6653f1 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:00:19 -0400 Subject: [PATCH 056/169] docs: remove outdated note (#20110) --- docs/docs/install/environment-variables.md | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 8d5ab55049..939c42439d 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -29,29 +29,26 @@ These environment variables are used by the `docker-compose.yml` file and do **N ## General -| Variable | Description | Default | Containers | Workers | -| :---------------------------------- | :---------------------------------------------------------------------------------------- | :---------------------------------: | :----------------------- | :----------------- | -| `TZ` | Timezone | \*1 | server | microservices | -| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | -| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload`\*3 | server | api, microservices | -| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | -| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | -| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | -| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | -| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | -| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | -| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | -| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | +| Variable | Description | Default | Containers | Workers | +| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- | +| `TZ` | Timezone | \*1 | server | microservices | +| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | +| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload` | server | api, microservices | +| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | +| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | +| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | +| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | +| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | +| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | +| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | +| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | \*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution. \*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead. -\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`. -It only needs to be set if the Immich deployment method is changing. - ## Workers | Variable | Description | Default | Containers | From a6759221727c64c82d7329cf276095129098e33a Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:52:59 -0400 Subject: [PATCH 057/169] fix: unset prewarn param (#20109) --- server/src/repositories/logging.repository.ts | 9 +++-- .../1750323941566-UnsetPrewarmDimParameter.ts | 35 +++++++++---------- .../1752759108283-ConvertToAbsolutePaths.ts | 3 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 1833168f3e..939ecb718f 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -85,8 +85,13 @@ export class LoggingRepository { this.logger = new MyConsoleLogger(cls, { context: LoggingRepository.name, color: !noColor }); } - static create() { - return new LoggingRepository(undefined, undefined); + static create(context?: string) { + const logger = new LoggingRepository(undefined, undefined); + if (context) { + logger.setContext(context); + } + + return logger; } setAppName(name: string): void { diff --git a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts index 776b51d1db..d687694490 100644 --- a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts +++ b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts @@ -1,25 +1,22 @@ import { Kysely, sql } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; -export async function up(qb: Kysely): Promise { - type Conf = { db: string; guc: string[] }; - const res = await sql` - select current_database() db, to_json(setconfig) guc - from pg_db_role_setting - where setdatabase = (select oid from pg_database where datname = current_database()) - and setrole = 0;`.execute(qb); - if (res.rows.length === 0) { - return; - } +const logger = LoggingRepository.create('Migrations'); - const { db, guc } = res.rows[0]; - await sql.raw(`alter database "${db}" reset all;`).execute(qb); - for (const parameter of guc) { - const [key, value] = parameter.split('='); - if (key === 'vchordrq.prewarm_dim') { - continue; - } - await sql.raw(`alter database "${db}" set ${key} to ${value};`).execute(qb); +export async function up(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db); + const databaseName = rows[0].db; + try { + await sql.raw(`ALTER DATABASE "${databaseName}" RESET vchordrq.prewarm_dim;`).execute(db); + } catch { + logger.warn('Failed to reset vchordrq.prewarm_dim, skipping'); } } -export async function down(): Promise {} +export async function down(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db); + const databaseName = rows[0].db; + await sql + .raw(`ALTER DATABASE "${databaseName}" SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536';`) + .execute(db); +} diff --git a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts index 68b0c7931e..839af40cf5 100644 --- a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts +++ b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts @@ -1,8 +1,7 @@ import { Kysely, sql } from 'kysely'; import { LoggingRepository } from 'src/repositories/logging.repository'; -const logger = LoggingRepository.create(); -logger.setContext('Migrations'); +const logger = LoggingRepository.create('Migrations'); export async function up(db: Kysely): Promise { if (process.env.IMMICH_MEDIA_LOCATION) { From bc8cb9b671412ff8aa2999a74ee45d5ff272b058 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:56:38 -0400 Subject: [PATCH 058/169] fix: default route permission (#20113) --- server/src/services/auth.service.spec.ts | 32 ++++++++++++++++++------ server/src/services/auth.service.ts | 7 +++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 129877bbdd..9f678162c6 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -459,18 +459,34 @@ describe(AuthService.name, () => { mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); - await expect( - sut.authenticate({ - headers: { 'x-api-key': 'auth_token' }, - queryParams: {}, - metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, - }), - ).rejects.toBeInstanceOf(ForbiddenException); + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, + }); + + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: asset.read'); + }); + + it('should default to requiring the all permission when omitted', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.AssetRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' }, + }); + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: all'); }); it('should return an auth dto', async () => { const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); + const authApiKey = factory.authApiKey({ permissions: [Permission.All] }); mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index a5b0de25cd..f757bf5a3e 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -174,7 +174,8 @@ export class AuthService extends BaseService { async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise { const authDto = await this.validate({ headers, queryParams }); - const { adminRoute, sharedLinkRoute, permission, uri } = metadata; + const { adminRoute, sharedLinkRoute, uri } = metadata; + const requestedPermission = metadata.permission ?? Permission.All; if (!authDto.user.isAdmin && adminRoute) { this.logger.warn(`Denied access to admin only route: ${uri}`); @@ -186,8 +187,8 @@ export class AuthService extends BaseService { throw new ForbiddenException('Forbidden'); } - if (authDto.apiKey && permission && !isGranted({ requested: [permission], current: authDto.apiKey.permissions })) { - throw new ForbiddenException(`Missing required permission: ${permission}`); + if (authDto.apiKey && !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })) { + throw new ForbiddenException(`Missing required permission: ${requestedPermission}`); } return authDto; From c1c9f30ea4b7879febc98188ba89a4a3f2ef1bc9 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:56:56 +0200 Subject: [PATCH 059/169] chore: migrate to immich/ui confirm modal (#20114) --- web/package-lock.json | 8 +-- web/package.json | 2 +- .../asset-viewer/editor/editor-panel.svelte | 3 +- .../photos-page/delete-asset-dialog.svelte | 3 +- .../shared-components/change-date.spec.ts | 4 +- .../shared-components/change-date.svelte | 4 +- .../shared-components/change-location.svelte | 2 +- web/src/lib/managers/modal-manager.svelte.ts | 2 +- .../AssetUpdateDecriptionConfirmModal.svelte | 3 +- web/src/lib/modals/ConfirmModal.svelte | 52 ------------------- web/src/lib/modals/JobCreateModal.svelte | 2 +- .../lib/modals/UserDeleteConfirmModal.svelte | 3 +- 12 files changed, 15 insertions(+), 73 deletions(-) delete mode 100644 web/src/lib/modals/ConfirmModal.svelte diff --git a/web/package-lock.json b/web/package-lock.json index 51f60a83c3..46b0a059ed 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.5", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1357,9 +1357,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.3.tgz", - "integrity": "sha512-YbYJSv3HqDu2+6MmiHhLThSessZ6HkoVOWun/ZoGb8mKj5x/ZZ4AyXGPIqbyKTamsjzbcD9FInij70G+m4egkg==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.5.tgz", + "integrity": "sha512-1wlFMmfDmtGC+Kcc8cYTT00mQaSumR41KEOOOmVn5Rw/8z9pUhpNY8mGl1AxY4qhtnaz+G3dH6vowYzL23D+YQ==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", diff --git a/web/package.json b/web/package.json index 753b0a15a6..7e1b769c52 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.5", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", diff --git a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte index f4d5c69c62..203f1c6587 100644 --- a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte +++ b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte @@ -1,10 +1,9 @@ - - onClose(false)} {size}> - - {#if promptSnippet}{@render promptSnippet()}{:else} -

{prompt}

- {/if} -
- - - - - - - -
diff --git a/web/src/lib/modals/JobCreateModal.svelte b/web/src/lib/modals/JobCreateModal.svelte index dbb97fdcf7..95751cbb98 100644 --- a/web/src/lib/modals/JobCreateModal.svelte +++ b/web/src/lib/modals/JobCreateModal.svelte @@ -4,9 +4,9 @@ notificationController, NotificationType, } from '$lib/components/shared-components/notification/notification'; - import ConfirmModal from '$lib/modals/ConfirmModal.svelte'; import { handleError } from '$lib/utils/handle-error'; import { createJob, ManualJobName } from '@immich/sdk'; + import { ConfirmModal } from '@immich/ui'; import { t } from 'svelte-i18n'; type Props = { onClose: (confirmed: boolean) => void }; diff --git a/web/src/lib/modals/UserDeleteConfirmModal.svelte b/web/src/lib/modals/UserDeleteConfirmModal.svelte index 3fde7a7ef9..bbb045ecb7 100644 --- a/web/src/lib/modals/UserDeleteConfirmModal.svelte +++ b/web/src/lib/modals/UserDeleteConfirmModal.svelte @@ -1,10 +1,9 @@ - -
-
- {#if showLogo} - - {:else if icon} - - {/if} -

- {title} -

-
- - -
diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index b079ecb241..b1746932b1 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -3,13 +3,12 @@ import { focusTrap } from '$lib/actions/focus-trap'; import Icon from '$lib/components/elements/icon.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte'; import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte'; import { user } from '$lib/stores/user.store'; import { userInteraction } from '$lib/stores/user.svelte'; import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index 9f36431f19..4c807e7652 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -3,14 +3,13 @@ import { focusOutside } from '$lib/actions/focus-outside'; import { shortcuts } from '$lib/actions/shortcut'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import SearchFilterModal from '$lib/modals/SearchFilterModal.svelte'; import { searchStore } from '$lib/stores/search.svelte'; import { handlePromiseError } from '$lib/utils'; import { generateId } from '$lib/utils/generate-id'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; - import { IconButton } from '@immich/ui'; + import { IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; import { onDestroy, tick } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 627292ea1b..f21cc7de4d 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -5,7 +5,6 @@ import Portal from '$lib/components/shared-components/portal/portal.svelte'; import SupporterBadge from '$lib/components/shared-components/side-bar/supporter-badge.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import PurchaseModal from '$lib/modals/PurchaseModal.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; import { preferences } from '$lib/stores/user.store'; @@ -13,11 +12,11 @@ import { handleError } from '$lib/utils/handle-error'; import { getButtonVisibility } from '$lib/utils/purchase-utils'; import { updateMyPreferences } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiInformationOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; - import { fade } from 'svelte/transition'; import { SvelteDate } from 'svelte/reactivity'; + import { fade } from 'svelte/transition'; let showMessage = $state(false); let hoverMessage = $state(false); diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 0a9f3d9d8c..3c3da8eb5f 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -1,6 +1,5 @@ -
+
{ + value = value.toLowerCase(); + return ([title, items]: [string, Permission[]]) => { + return title.toLowerCase().includes(value) || items.some((item) => item.toLowerCase().includes(value)); + }; + }; interface Props { apiKey: { name: string; permissions: Permission[] }; @@ -20,137 +26,26 @@ } let { apiKey = $bindable(), title, cancelText = $t('cancel'), submitText = $t('save'), onClose }: Props = $props(); + let name = $derived(apiKey.name); let selectedItems: Permission[] = $state(apiKey.permissions); let selectAllItems = $derived(selectedItems.length === Object.keys(Permission).length - 1); - const permissions: Map = new SvelteMap(); + const permissions: Record = {}; + for (const permission of Object.values(Permission)) { + if (permission === Permission.All) { + continue; + } - permissions.set('activity', [ - Permission.ActivityCreate, - Permission.ActivityRead, - Permission.ActivityUpdate, - Permission.ActivityDelete, - Permission.ActivityStatistics, - ]); + const [group] = permission.split('.'); + if (!permissions[group]) { + permissions[group] = []; + } + permissions[group].push(permission); + } - permissions.set('api_key', [ - Permission.ApiKeyCreate, - Permission.ApiKeyRead, - Permission.ApiKeyUpdate, - Permission.ApiKeyDelete, - ]); - - permissions.set('asset', [ - Permission.AssetRead, - Permission.AssetUpdate, - Permission.AssetDelete, - Permission.AssetShare, - Permission.AssetView, - Permission.AssetDownload, - Permission.AssetUpload, - ]); - - permissions.set('album', [ - Permission.AlbumCreate, - Permission.AlbumRead, - Permission.AlbumUpdate, - Permission.AlbumDelete, - Permission.AlbumStatistics, - - Permission.AlbumAddAsset, - Permission.AlbumRemoveAsset, - Permission.AlbumShare, - Permission.AlbumDownload, - ]); - - permissions.set('auth_device', [Permission.AuthDeviceDelete]); - - permissions.set('archive', [Permission.ArchiveRead]); - - permissions.set('face', [Permission.FaceCreate, Permission.FaceRead, Permission.FaceUpdate, Permission.FaceDelete]); - - permissions.set('library', [ - Permission.LibraryCreate, - Permission.LibraryRead, - Permission.LibraryUpdate, - Permission.LibraryDelete, - Permission.LibraryStatistics, - ]); - - permissions.set('timeline', [Permission.TimelineRead, Permission.TimelineDownload]); - - permissions.set('memory', [ - Permission.MemoryCreate, - Permission.MemoryRead, - Permission.MemoryUpdate, - Permission.MemoryDelete, - ]); - - permissions.set('notification', [ - Permission.NotificationCreate, - Permission.NotificationRead, - Permission.NotificationUpdate, - Permission.NotificationDelete, - ]); - - permissions.set('partner', [ - Permission.PartnerCreate, - Permission.PartnerRead, - Permission.PartnerUpdate, - Permission.PartnerDelete, - ]); - - permissions.set('person', [ - Permission.PersonCreate, - Permission.PersonRead, - Permission.PersonUpdate, - Permission.PersonDelete, - Permission.PersonStatistics, - Permission.PersonMerge, - Permission.PersonReassign, - ]); - - permissions.set('session', [ - Permission.SessionCreate, - Permission.SessionRead, - Permission.SessionUpdate, - Permission.SessionDelete, - Permission.SessionLock, - ]); - - permissions.set('sharedLink', [ - Permission.SharedLinkCreate, - Permission.SharedLinkRead, - Permission.SharedLinkUpdate, - Permission.SharedLinkDelete, - ]); - - permissions.set('stack', [ - Permission.StackCreate, - Permission.StackRead, - Permission.StackUpdate, - Permission.StackDelete, - ]); - - permissions.set('systemConfig', [Permission.SystemConfigRead, Permission.SystemConfigUpdate]); - - permissions.set('systemMetadata', [Permission.SystemMetadataRead, Permission.SystemMetadataUpdate]); - - permissions.set('tag', [ - Permission.TagCreate, - Permission.TagRead, - Permission.TagUpdate, - Permission.TagDelete, - Permission.TagAsset, - ]); - - permissions.set('adminUser', [ - Permission.AdminUserCreate, - Permission.AdminUserRead, - Permission.AdminUserUpdate, - Permission.AdminUserDelete, - ]); + let searchValue = $state(''); + let filteredResults = $derived(Object.entries(permissions).filter(matches(searchValue))); const handleSelectItems = (permissions: Permission[]) => { selectedItems = Array.from(new Set([...selectedItems, ...permissions])); @@ -177,9 +72,9 @@ }); } else { if (selectAllItems) { - onClose({ name: apiKey.name, permissions: [Permission.All] }); + onClose({ name, permissions: [Permission.All] }); } else { - onClose({ name: apiKey.name, permissions: selectedItems }); + onClose({ name, permissions: selectedItems }); } } }; @@ -200,22 +95,37 @@
- - + + +
- -
- -
{/await} {:then { default: Map }} - + {/await}
From 7e7b8da1286992072bf08f9722411d096acd2356 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 12:41:22 -0400 Subject: [PATCH 111/169] fix: debug source maps (#20363) --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ed3da9f667..0cc9b256ca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "restart": true, "port": 9231, "name": "Immich API Server", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" }, { @@ -16,7 +16,7 @@ "restart": true, "port": 9230, "name": "Immich Workers", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" } ] From 16b14b390ff7128dc3503b83d2b38c45a3e5a361 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 13:30:49 -0400 Subject: [PATCH 112/169] fix: file samples (#20364) --- server/src/queries/asset.repository.sql | 2 ++ server/src/repositories/asset.repository.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 9425bd9a11..185afa562b 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -189,6 +189,8 @@ select ) as "files" from "asset" +where + "asset"."libraryId" is null limit 3 diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index edbafaa22d..286a66e9af 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -349,6 +349,7 @@ export class AssetRepository { 'files', ), ]) + .where('asset.libraryId', 'is', null) .limit(sql.lit(3)) .execute(); } From 9b3718120b055813cf2ffc7ca65eced276ed08b7 Mon Sep 17 00:00:00 2001 From: Jed-Giblin Date: Mon, 28 Jul 2025 14:16:55 -0400 Subject: [PATCH 113/169] feat: shared links custom URL (#19999) * feat: custom url for shared links * feat: use a separate route and query param --------- Co-authored-by: Jason Rasmussen --- i18n/en.json | 4 +- mobile/openapi/lib/api/albums_api.dart | 26 +++- mobile/openapi/lib/api/assets_api.dart | 78 +++++++--- mobile/openapi/lib/api/download_api.dart | 26 +++- mobile/openapi/lib/api/shared_links_api.dart | 39 +++-- mobile/openapi/lib/api/timeline_api.dart | 26 +++- .../lib/model/shared_link_create_dto.dart | 25 ++-- .../lib/model/shared_link_edit_dto.dart | 29 ++-- .../lib/model/shared_link_response_dto.dart | 14 +- open-api/immich-openapi-specs.json | 137 ++++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 93 ++++++++---- server/src/database.ts | 1 + server/src/dtos/shared-link.dto.ts | 32 ++-- server/src/enum.ts | 2 + server/src/middleware/auth.guard.ts | 5 +- server/src/queries/shared.link.repository.sql | 44 +++++- .../repositories/shared-link.repository.ts | 15 +- .../1753471866748-AddSharedLinkSlug.ts | 11 ++ server/src/schema/tables/shared-link.table.ts | 3 + server/src/services/api.service.ts | 2 +- server/src/services/auth.service.spec.ts | 46 +++++- server/src/services/auth.service.ts | 38 +++-- .../src/services/shared-link.service.spec.ts | 5 + server/src/services/shared-link.service.ts | 68 +++++---- server/test/fixtures/shared-link.stub.ts | 8 + .../components/album-page/albums-list.svelte | 2 +- .../actions/download-action.svelte | 2 +- .../asset-viewer/actions/share-action.svelte | 2 +- .../asset-viewer/asset-viewer.svelte | 4 +- .../detail-panel-star-rating.svelte | 2 +- .../asset-viewer/detail-panel-tags.svelte | 2 +- .../asset-viewer/detail-panel.svelte | 4 +- .../asset-viewer/image-panorama-viewer.svelte | 2 +- .../assets/thumbnail/thumbnail.svelte | 4 +- .../memory-page/memory-viewer.svelte | 2 +- .../pages/SharedLinkErrorPage.svelte | 14 ++ .../components/pages/SharedLinkPage.svelte | 108 ++++++++++++++ .../actions/create-shared-link.svelte | 2 +- .../actions/download-action.svelte | 4 +- .../actions/link-live-photo-action.svelte | 6 +- .../actions/remove-from-shared-link.svelte | 2 +- .../components/photos-page/asset-grid.svelte | 8 +- .../individual-shared-viewer.svelte | 8 +- .../drag-and-drop-upload-overlay.svelte | 2 +- .../actions/shared-link-copy.svelte | 2 +- .../sharedlinks-page/shared-link-card.svelte | 13 +- web/src/lib/managers/auth-manager.svelte.ts | 3 +- .../internal/load-support.svelte.ts | 5 +- .../timeline-manager.svelte.ts | 4 +- web/src/lib/modals/AlbumShareModal.svelte | 2 +- .../lib/modals/SharedLinkCreateModal.svelte | 117 +++++++-------- web/src/lib/stores/asset-viewing.store.ts | 2 +- web/src/lib/utils.ts | 11 +- web/src/lib/utils/asset-utils.ts | 17 ++- web/src/lib/utils/file-uploader.ts | 10 +- web/src/lib/utils/navigation.ts | 3 +- web/src/lib/utils/shared-links.ts | 58 ++++++++ .../[[assetId=id]]/+page.svelte | 3 +- web/src/routes/(user)/s/[slug]/+error.svelte | 5 + .../[[assetId=id]]/+page.svelte | 12 ++ .../[[photos=photos]]/[[assetId=id]]/+page.ts | 4 + .../routes/(user)/share/[key]/+error.svelte | 13 +- .../[[assetId=id]]/+page.svelte | 93 +----------- .../[[photos=photos]]/[[assetId=id]]/+page.ts | 44 +----- .../factories/shared-link-factory.ts | 1 + 65 files changed, 947 insertions(+), 432 deletions(-) create mode 100644 server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts create mode 100644 web/src/lib/components/pages/SharedLinkErrorPage.svelte create mode 100644 web/src/lib/components/pages/SharedLinkPage.svelte create mode 100644 web/src/lib/utils/shared-links.ts create mode 100644 web/src/routes/(user)/s/[slug]/+error.svelte create mode 100644 web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte create mode 100644 web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts diff --git a/i18n/en.json b/i18n/en.json index cfc8ffccee..39baff3381 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -725,6 +725,7 @@ "current_server_address": "Current server address", "custom_locale": "Custom Locale", "custom_locale_description": "Format dates and numbers based on the language and the region", + "custom_url": "Custom URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Dark", @@ -1172,7 +1173,6 @@ "light": "Light", "like_deleted": "Like deleted", "link_motion_video": "Link motion video", - "link_options": "Link options", "link_to_oauth": "Link to OAuth", "linked_oauth_account": "Linked OAuth account", "list": "List", @@ -1745,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Copied to clipboard", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Error while creating shared link", + "shared_link_custom_url_description": "Access this shared link with a custom URL", "shared_link_edit_description_hint": "Enter the share description", "shared_link_edit_expire_after_option_day": "1 day", "shared_link_edit_expire_after_option_days": "{count} days", @@ -1770,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Manage Shared links", "shared_link_options": "Shared link options", + "shared_link_password_description": "Require a password to access this shared link", "shared_links": "Shared links", "shared_links_description": "Share photos and videos with a link", "shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}", diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index a8c518ace2..fa7a562adb 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -24,7 +24,9 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { - final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { + final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -225,8 +232,10 @@ class AlbumsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfoWithHttpInfo(String id, { String? key, bool? withoutAssets, }) async { + Future getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -241,6 +250,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (withoutAssets != null) { queryParams.addAll(_queryParams('', 'withoutAssets', withoutAssets)); } @@ -265,9 +277,11 @@ class AlbumsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfo(String id, { String? key, bool? withoutAssets, }) async { - final response = await getAlbumInfoWithHttpInfo(id, key: key, withoutAssets: withoutAssets, ); + Future getAlbumInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { + final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, withoutAssets: withoutAssets, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index db6a2e78a3..3cb62785be 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -173,7 +173,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future downloadAssetWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future downloadAssetWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -188,6 +190,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -208,8 +213,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future downloadAsset(String id, { String? key, }) async { - final response = await downloadAssetWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future downloadAsset(String id, { String? key, String? slug, }) async { + final response = await downloadAssetWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -289,7 +296,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future getAssetInfoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -304,6 +313,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -324,8 +336,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future getAssetInfo(String id, { String? key, }) async { - final response = await getAssetInfoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future getAssetInfo(String id, { String? key, String? slug, }) async { + final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -469,7 +483,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future playAssetVideoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/video/playback' .replaceAll('{id}', id); @@ -484,6 +500,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -504,8 +523,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future playAssetVideo(String id, { String? key, }) async { - final response = await playAssetVideoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future playAssetVideo(String id, { String? key, String? slug, }) async { + final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -541,10 +562,12 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -559,6 +582,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['multipart/form-data']; @@ -628,11 +654,13 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { - final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, ); + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -791,6 +819,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -805,7 +835,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -819,6 +849,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (xImmichChecksum != null) { headerParams[r'x-immich-checksum'] = parameterToString(xImmichChecksum); @@ -903,6 +936,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -917,8 +952,8 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -940,7 +975,9 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, }) async { + /// + /// * [String] slug: + Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/thumbnail' .replaceAll('{id}', id); @@ -958,6 +995,9 @@ class AssetsApi { if (size != null) { queryParams.addAll(_queryParams('', 'size', size)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -980,8 +1020,10 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAsset(String id, { String? key, AssetMediaSize? size, }) async { - final response = await viewAssetWithHttpInfo(id, key: key, size: size, ); + /// + /// * [String] slug: + Future viewAsset(String id, { String? key, AssetMediaSize? size, String? slug, }) async { + final response = await viewAssetWithHttpInfo(id, key: key, size: size, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 3b11c2f630..675996f932 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -22,7 +22,9 @@ class DownloadApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/archive'; @@ -36,6 +38,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -56,8 +61,10 @@ class DownloadApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -77,7 +84,9 @@ class DownloadApi { /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { + /// + /// * [String] slug: + Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/info'; @@ -91,6 +100,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -111,8 +123,10 @@ class DownloadApi { /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { - final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, ); + /// + /// * [String] slug: + Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { + final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index 5bac8988dc..dd372b962b 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -24,7 +24,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -187,8 +194,10 @@ class SharedLinksApi { /// /// * [String] password: /// + /// * [String] slug: + /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async { + Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? slug, String? token, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -205,6 +214,9 @@ class SharedLinksApi { if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } @@ -229,9 +241,11 @@ class SharedLinksApi { /// /// * [String] password: /// + /// * [String] slug: + /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, token: token, ); + Future getMySharedLink({ String? key, String? password, String? slug, String? token, }) async { + final response = await getMySharedLinkWithHttpInfo( key: key, password: password, slug: slug, token: token, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -341,7 +355,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -356,6 +372,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -378,8 +397,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 042bc70401..2d3ced610b 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -39,6 +39,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -53,7 +55,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -82,6 +84,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -135,6 +140,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -149,8 +156,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -184,6 +191,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -198,7 +207,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -227,6 +236,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -276,6 +288,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -290,8 +304,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/model/shared_link_create_dto.dart b/mobile/openapi/lib/model/shared_link_create_dto.dart index bc96b31fd2..644227bd6e 100644 --- a/mobile/openapi/lib/model/shared_link_create_dto.dart +++ b/mobile/openapi/lib/model/shared_link_create_dto.dart @@ -21,6 +21,7 @@ class SharedLinkCreateDto { this.expiresAt, this.password, this.showMetadata = true, + this.slug, required this.type, }); @@ -44,26 +45,16 @@ class SharedLinkCreateDto { List assetIds; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// 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; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; bool showMetadata; + String? slug; + SharedLinkType type; @override @@ -76,6 +67,7 @@ class SharedLinkCreateDto { other.expiresAt == expiresAt && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.type == type; @override @@ -89,10 +81,11 @@ class SharedLinkCreateDto { (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (type.hashCode); @override - String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, type=$type]'; + String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug, type=$type]'; Map toJson() { final json = {}; @@ -124,6 +117,11 @@ class SharedLinkCreateDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } json[r'type'] = this.type; return json; } @@ -147,6 +145,7 @@ class SharedLinkCreateDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata') ?? true, + slug: mapValueOfType(json, r'slug'), type: SharedLinkType.fromJson(json[r'type'])!, ); } diff --git a/mobile/openapi/lib/model/shared_link_edit_dto.dart b/mobile/openapi/lib/model/shared_link_edit_dto.dart index a394ba9b3b..f13bc6977b 100644 --- a/mobile/openapi/lib/model/shared_link_edit_dto.dart +++ b/mobile/openapi/lib/model/shared_link_edit_dto.dart @@ -20,6 +20,7 @@ class SharedLinkEditDto { this.expiresAt, this.password, this.showMetadata, + this.slug, }); /// @@ -47,22 +48,10 @@ class SharedLinkEditDto { /// bool? changeExpiryTime; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// 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; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; /// @@ -73,6 +62,8 @@ class SharedLinkEditDto { /// bool? showMetadata; + String? slug; + @override bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto && other.allowDownload == allowDownload && @@ -81,7 +72,8 @@ class SharedLinkEditDto { other.description == description && other.expiresAt == expiresAt && other.password == password && - other.showMetadata == showMetadata; + other.showMetadata == showMetadata && + other.slug == slug; @override int get hashCode => @@ -92,10 +84,11 @@ class SharedLinkEditDto { (description == null ? 0 : description!.hashCode) + (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + - (showMetadata == null ? 0 : showMetadata!.hashCode); + (showMetadata == null ? 0 : showMetadata!.hashCode) + + (slug == null ? 0 : slug!.hashCode); @override - String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata]'; + String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug]'; Map toJson() { final json = {}; @@ -134,6 +127,11 @@ class SharedLinkEditDto { } else { // json[r'showMetadata'] = null; } + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } return json; } @@ -153,6 +151,7 @@ class SharedLinkEditDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata'), + slug: mapValueOfType(json, r'slug'), ); } return null; diff --git a/mobile/openapi/lib/model/shared_link_response_dto.dart b/mobile/openapi/lib/model/shared_link_response_dto.dart index 9cc8b3ac80..d81e1dfa31 100644 --- a/mobile/openapi/lib/model/shared_link_response_dto.dart +++ b/mobile/openapi/lib/model/shared_link_response_dto.dart @@ -24,6 +24,7 @@ class SharedLinkResponseDto { required this.key, required this.password, required this.showMetadata, + required this.slug, this.token, required this.type, required this.userId, @@ -57,6 +58,8 @@ class SharedLinkResponseDto { bool showMetadata; + String? slug; + String? token; SharedLinkType type; @@ -76,6 +79,7 @@ class SharedLinkResponseDto { other.key == key && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.token == token && other.type == type && other.userId == userId; @@ -94,12 +98,13 @@ class SharedLinkResponseDto { (key.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (token == null ? 0 : token!.hashCode) + (type.hashCode) + (userId.hashCode); @override - String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, token=$token, type=$type, userId=$userId]'; + String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, slug=$slug, token=$token, type=$type, userId=$userId]'; Map toJson() { final json = {}; @@ -130,6 +135,11 @@ class SharedLinkResponseDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } if (this.token != null) { json[r'token'] = this.token; } else { @@ -160,6 +170,7 @@ class SharedLinkResponseDto { key: mapValueOfType(json, r'key')!, password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata')!, + slug: mapValueOfType(json, r'slug'), token: mapValueOfType(json, r'token'), type: SharedLinkType.fromJson(json[r'type'])!, userId: mapValueOfType(json, r'userId')!, @@ -220,6 +231,7 @@ class SharedLinkResponseDto { 'key', 'password', 'showMetadata', + 'slug', 'type', 'userId', }; diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 576592413d..5000eaa5c7 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -956,6 +956,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "withoutAssets", "required": false, @@ -1116,6 +1124,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -1550,6 +1566,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "x-immich-checksum", "in": "header", @@ -1929,6 +1953,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2029,6 +2061,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2079,6 +2119,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2151,6 +2199,14 @@ "schema": { "$ref": "#/components/schemas/AssetMediaSize" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2202,6 +2258,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2605,6 +2669,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2657,6 +2729,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6217,6 +6297,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "token", "required": false, @@ -6399,6 +6487,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6460,6 +6556,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -7730,6 +7834,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -7875,6 +7987,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -13027,6 +13147,7 @@ "type": "array" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -13036,12 +13157,17 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "default": true, "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "type": { "allOf": [ { @@ -13068,6 +13194,7 @@ "type": "boolean" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -13076,10 +13203,15 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "type": "boolean" + }, + "slug": { + "nullable": true, + "type": "string" } }, "type": "object" @@ -13127,6 +13259,10 @@ "showMetadata": { "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "token": { "nullable": true, "type": "string" @@ -13153,6 +13289,7 @@ "key", "password", "showMetadata", + "slug", "type", "userId" ], diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 7c030d43c2..063fc86782 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1199,6 +1199,7 @@ export type SharedLinkResponseDto = { key: string; password: string | null; showMetadata: boolean; + slug: string | null; token?: string | null; "type": SharedLinkType; userId: string; @@ -1208,10 +1209,11 @@ export type SharedLinkCreateDto = { allowDownload?: boolean; allowUpload?: boolean; assetIds?: string[]; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; "type": SharedLinkType; }; export type SharedLinkEditDto = { @@ -1221,10 +1223,11 @@ export type SharedLinkEditDto = { Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. */ changeExpiryTime?: boolean; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; }; export type AssetIdsResponseDto = { assetId: string; @@ -1821,9 +1824,10 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } -export function getAlbumInfo({ id, key, withoutAssets }: { +export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; + slug?: string; withoutAssets?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -1831,6 +1835,7 @@ export function getAlbumInfo({ id, key, withoutAssets }: { data: AlbumResponseDto; }>(`/albums/${encodeURIComponent(id)}${QS.query(QS.explode({ key, + slug, withoutAssets }))}`, { ...opts @@ -1862,16 +1867,18 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function addAssetsToAlbum({ id, key, bulkIdsDto }: { +export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; + slug?: string; bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: BulkIdResponseDto[]; }>(`/albums/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", @@ -1971,8 +1978,9 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } -export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { +export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; + slug?: string; xImmichChecksum?: string; assetMediaCreateDto: AssetMediaCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1980,7 +1988,8 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { status: 201; data: AssetMediaResponseDto; }>(`/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "POST", @@ -2082,15 +2091,17 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } -export function getAssetInfo({ id, key }: { +export function getAssetInfo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetResponseDto; }>(`/assets/${encodeURIComponent(id)}${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2108,15 +2119,17 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } -export function downloadAsset({ id, key }: { +export function downloadAsset({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2124,46 +2137,52 @@ export function downloadAsset({ id, key }: { /** * replaceAsset */ -export function replaceAsset({ id, key, assetMediaReplaceDto }: { +export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { id: string; key?: string; + slug?: string; assetMediaReplaceDto: AssetMediaReplaceDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetMediaResponseDto; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "PUT", body: assetMediaReplaceDto }))); } -export function viewAsset({ id, key, size }: { +export function viewAsset({ id, key, size, slug }: { id: string; key?: string; size?: AssetMediaSize; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/thumbnail${QS.query(QS.explode({ key, - size + size, + slug }))}`, { ...opts })); } -export function playAssetVideo({ id, key }: { +export function playAssetVideo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/video/playback${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2272,30 +2291,34 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } -export function downloadArchive({ key, assetIdsDto }: { +export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/download/archive${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: assetIdsDto }))); } -export function getDownloadInfo({ key, downloadInfoDto }: { +export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; + slug?: string; downloadInfoDto: DownloadInfoDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 201; data: DownloadResponseDto; }>(`/download/info${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", @@ -3230,9 +3253,10 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, token }: { +export function getMySharedLink({ key, password, slug, token }: { key?: string; password?: string; + slug?: string; token?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -3241,6 +3265,7 @@ export function getMySharedLink({ key, password, token }: { }>(`/shared-links/me${QS.query(QS.explode({ key, password, + slug, token }))}`, { ...opts @@ -3277,32 +3302,36 @@ export function updateSharedLink({ id, sharedLinkEditDto }: { body: sharedLinkEditDto }))); } -export function removeSharedLinkAssets({ id, key, assetIdsDto }: { +export function removeSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "DELETE", body: assetIdsDto }))); } -export function addSharedLinkAssets({ id, key, assetIdsDto }: { +export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", @@ -3611,13 +3640,14 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { +export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; timeBucket: string; userId?: string; @@ -3635,6 +3665,7 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers key, order, personId, + slug, tagId, timeBucket, userId, @@ -3645,13 +3676,14 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers ...opts })); } -export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, tagId, userId, visibility, withPartners, withStacked }: { +export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; userId?: string; visibility?: AssetVisibility; @@ -3668,6 +3700,7 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per key, order, personId, + slug, tagId, userId, visibility, diff --git a/server/src/database.ts b/server/src/database.ts index 53c39b7383..44bdefa080 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -192,6 +192,7 @@ export type SharedLink = { showExif: boolean; type: SharedLinkType; userId: string; + slug: string | null; }; export type Album = Selectable & { diff --git a/server/src/dtos/shared-link.dto.ts b/server/src/dtos/shared-link.dto.ts index 299590c0e3..011707f1f7 100644 --- a/server/src/dtos/shared-link.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -22,13 +22,17 @@ export class SharedLinkCreateDto { @ValidateUUID({ optional: true }) albumId?: string; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - description?: string; + description?: string | null; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - password?: string; + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @ValidateDate({ optional: true, nullable: true }) expiresAt?: Date | null = null; @@ -44,16 +48,22 @@ export class SharedLinkCreateDto { } export class SharedLinkEditDto { - @Optional() - description?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + description?: string | null; - @Optional() - password?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @Optional({ nullable: true }) expiresAt?: Date | null; - @Optional() + @ValidateBoolean({ optional: true }) allowUpload?: boolean; @ValidateBoolean({ optional: true }) @@ -99,6 +109,8 @@ export class SharedLinkResponseDto { allowDownload!: boolean; showMetadata!: boolean; + + slug!: string | null; } export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { @@ -118,6 +130,7 @@ export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } @@ -141,5 +154,6 @@ export function mapSharedLinkWithoutMetadata(sharedLink: SharedLink): SharedLink allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } diff --git a/server/src/enum.ts b/server/src/enum.ts index 70e94ed43f..d17b5dc901 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -17,12 +17,14 @@ export enum ImmichHeader { UserToken = 'x-immich-user-token', SessionToken = 'x-immich-session-token', SharedLinkKey = 'x-immich-share-key', + SharedLinkSlug = 'x-immich-share-slug', Checksum = 'x-immich-checksum', Cid = 'x-immich-cid', } export enum ImmichQuery { SharedLinkKey = 'key', + SharedLinkSlug = 'slug', ApiKey = 'apiKey', SessionKey = 'sessionKey', } diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 238f99257a..69b3cb5ecc 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -28,7 +28,10 @@ export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator = ]; if ((options as SharedLinkRoute)?.sharedLink) { - decorators.push(ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false })); + decorators.push( + ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), + ApiQuery({ name: ImmichQuery.SharedLinkSlug, type: String, required: false }), + ); } return applyDecorators(...decorators); diff --git a/server/src/queries/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql index ed3507fa2f..0e13b98b5d 100644 --- a/server/src/queries/shared.link.repository.sql +++ b/server/src/queries/shared.link.repository.sql @@ -188,9 +188,47 @@ from "shared_link" left join "album" on "album"."id" = "shared_link"."albumId" where - "shared_link"."key" = $1 - and "album"."deletedAt" is null + "album"."deletedAt" is null and ( - "shared_link"."type" = $2 + "shared_link"."type" = $1 or "album"."id" is not null ) + and "shared_link"."key" = $2 + +-- SharedLinkRepository.getBySlug +select + "shared_link"."id", + "shared_link"."userId", + "shared_link"."expiresAt", + "shared_link"."showExif", + "shared_link"."allowUpload", + "shared_link"."allowDownload", + "shared_link"."password", + ( + select + to_json(obj) + from + ( + select + "user"."id", + "user"."name", + "user"."email", + "user"."isAdmin", + "user"."quotaUsageInBytes", + "user"."quotaSizeInBytes" + from + "user" + where + "user"."id" = "shared_link"."userId" + ) as obj + ) as "user" +from + "shared_link" + left join "album" on "album"."id" = "shared_link"."albumId" +where + "album"."deletedAt" is null + and ( + "shared_link"."type" = $1 + or "album"."id" is not null + ) + and "shared_link"."slug" = $2 diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index d5fb3be47d..54eab7c86f 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -173,10 +173,18 @@ export class SharedLinkRepository { } @GenerateSql({ params: [DummyValue.BUFFER] }) - async getByKey(key: Buffer) { + getByKey(key: Buffer) { + return this.authBuilder().where('shared_link.key', '=', key).executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.BUFFER] }) + getBySlug(slug: string) { + return this.authBuilder().where('shared_link.slug', '=', slug).executeTakeFirst(); + } + + private authBuilder() { return this.db .selectFrom('shared_link') - .where('shared_link.key', '=', key) .leftJoin('album', 'album.id', 'shared_link.albumId') .where('album.deletedAt', 'is', null) .select((eb) => [ @@ -185,8 +193,7 @@ export class SharedLinkRepository { eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'), ).as('user'), ]) - .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])) - .executeTakeFirst(); + .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])); } async create(entity: Insertable & { assetIds?: string[] }) { diff --git a/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts new file mode 100644 index 0000000000..1d77eddd07 --- /dev/null +++ b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts @@ -0,0 +1,11 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" ADD "slug" character varying;`.execute(db); + await sql`ALTER TABLE "shared_link" ADD CONSTRAINT "shared_link_slug_uq" UNIQUE ("slug");`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" DROP CONSTRAINT "shared_link_slug_uq";`.execute(db); + await sql`ALTER TABLE "shared_link" DROP COLUMN "slug";`.execute(db); +} diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 40fd2bf6a9..80e2d7cdf4 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -48,4 +48,7 @@ export class SharedLinkTable { @Column({ type: 'character varying', nullable: true }) password!: string | null; + + @Column({ type: 'character varying', nullable: true, unique: true }) + slug!: string | null; } diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index 27a776e867..ced74482c2 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -80,7 +80,7 @@ export class ApiService { if (shareMatches) { try { const key = shareMatches[1]; - const auth = await this.authService.validateSharedLink(key); + const auth = await this.authService.validateSharedLinkKey(key); const meta = await this.sharedLinkService.getMetadataTags( auth, request.host ? `${request.protocol}://${request.host}` : undefined, diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 9f678162c6..58c2542310 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -322,15 +322,18 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('base64url'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('base64url') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); }); it('should accept a hex key', async () => { @@ -340,15 +343,50 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('hex'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('hex') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); + }); + }); + + describe('validate - shared link slug', () => { + it('should not accept a non-existent slug', async () => { + mocks.sharedLink.getBySlug.mockResolvedValue(void 0); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should accept a valid slug', async () => { + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, slug: 'slug-123', user } as any; + + mocks.sharedLink.getBySlug.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug-123' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getBySlug).toHaveBeenCalledWith('slug-123'); }); }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index f757bf5a3e..fcaeb06af0 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -6,7 +6,7 @@ import { IncomingHttpHeaders } from 'node:http'; import { join } from 'node:path'; import { LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { UserAdmin } from 'src/database'; +import { AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { AuthDto, AuthStatusResponseDto, @@ -196,6 +196,7 @@ export class AuthService extends BaseService { private async validate({ headers, queryParams }: Omit): Promise { const shareKey = (headers[ImmichHeader.SharedLinkKey] || queryParams[ImmichQuery.SharedLinkKey]) as string; + const shareSlug = (headers[ImmichHeader.SharedLinkSlug] || queryParams[ImmichQuery.SharedLinkSlug]) as string; const session = (headers[ImmichHeader.UserToken] || headers[ImmichHeader.SessionToken] || queryParams[ImmichQuery.SessionKey] || @@ -204,7 +205,11 @@ export class AuthService extends BaseService { const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string; if (shareKey) { - return this.validateSharedLink(shareKey); + return this.validateSharedLinkKey(shareKey); + } + + if (shareSlug) { + return this.validateSharedLinkSlug(shareSlug); } if (session) { @@ -403,18 +408,33 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.OAuthCodeVerifier] || null; } - async validateSharedLink(key: string | string[]): Promise { + async validateSharedLinkKey(key: string | string[]): Promise { key = Array.isArray(key) ? key[0] : key; const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); const sharedLink = await this.sharedLinkRepository.getByKey(bytes); - if (sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) { - return { - user: sharedLink.user, - sharedLink, - }; + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share key'); } - throw new UnauthorizedException('Invalid share key'); + + return { user: sharedLink.user, sharedLink }; + } + + async validateSharedLinkSlug(slug: string | string[]): Promise { + slug = Array.isArray(slug) ? slug[0] : slug; + + const sharedLink = await this.sharedLinkRepository.getBySlug(slug); + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share slug'); + } + + return { user: sharedLink.user, sharedLink }; + } + + private isValidSharedLink( + sharedLink?: AuthSharedLink & { user: AuthUser | null }, + ): sharedLink is AuthSharedLink & { user: AuthUser } { + return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date()); } private async validateApiKey(key: string): Promise { diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 8e09580d55..9483cdddff 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -136,6 +136,7 @@ describe(SharedLinkService.name, () => { allowUpload: true, description: null, expiresAt: null, + slug: null, showExif: true, key: Buffer.from('random-bytes', 'utf8'), }); @@ -163,6 +164,7 @@ describe(SharedLinkService.name, () => { userId: authStub.admin.user.id, albumId: null, allowDownload: true, + slug: null, allowUpload: true, assetIds: [assetStub.image.id], description: null, @@ -199,6 +201,7 @@ describe(SharedLinkService.name, () => { description: null, expiresAt: null, showExif: false, + slug: null, key: Buffer.from('random-bytes', 'utf8'), }); }); @@ -223,6 +226,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ id: sharedLinkStub.valid.id, + slug: null, userId: authStub.user1.user.id, allowDownload: false, }); @@ -277,6 +281,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.update).toHaveBeenCalled(); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, + slug: null, assetIds: ['asset-3'], }); }); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 9f8e238c43..096739d056 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { PostgresError } from 'postgres'; import { SharedLink } from 'src/database'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; @@ -64,36 +65,53 @@ export class SharedLinkService extends BaseService { } } - const sharedLink = await this.sharedLinkRepository.create({ - key: this.cryptoRepository.randomBytes(50), - userId: auth.user.id, - type: dto.type, - albumId: dto.albumId || null, - assetIds: dto.assetIds, - description: dto.description || null, - password: dto.password, - expiresAt: dto.expiresAt || null, - allowUpload: dto.allowUpload ?? true, - allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), - showExif: dto.showMetadata ?? true, - }); + try { + const sharedLink = await this.sharedLinkRepository.create({ + key: this.cryptoRepository.randomBytes(50), + userId: auth.user.id, + type: dto.type, + albumId: dto.albumId || null, + assetIds: dto.assetIds, + description: dto.description || null, + password: dto.password, + expiresAt: dto.expiresAt || null, + allowUpload: dto.allowUpload ?? true, + allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), + showExif: dto.showMetadata ?? true, + slug: dto.slug || null, + }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } + } + + private handleError(error: unknown): never { + if ((error as PostgresError).constraint_name === 'shared_link_slug_uq') { + throw new BadRequestException('Shared link with this slug already exists'); + } + throw error; } async update(auth: AuthDto, id: string, dto: SharedLinkEditDto) { await this.findOrFail(auth.user.id, id); - const sharedLink = await this.sharedLinkRepository.update({ - id, - userId: auth.user.id, - description: dto.description, - password: dto.password, - expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, - allowUpload: dto.allowUpload, - allowDownload: dto.allowDownload, - showExif: dto.showMetadata, - }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + try { + const sharedLink = await this.sharedLinkRepository.update({ + id, + userId: auth.user.id, + description: dto.description, + password: dto.password, + expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, + allowUpload: dto.allowUpload, + allowDownload: dto.allowDownload, + showExif: dto.showMetadata, + slug: dto.slug || null, + }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } } async remove(auth: AuthDto, id: string): Promise { diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 47201a5b3b..1cd36f1f23 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -118,6 +118,7 @@ export const sharedLinkStub = { description: null, assets: [assetStub.image], password: 'password', + slug: null, }), valid: Object.freeze({ id: '123', @@ -135,6 +136,7 @@ export const sharedLinkStub = { password: null, assets: [] as MapAsset[], album: null, + slug: null, }), expired: Object.freeze({ id: '123', @@ -152,6 +154,7 @@ export const sharedLinkStub = { albumId: null, assets: [] as MapAsset[], album: null, + slug: null, }), readonlyNoExif: Object.freeze({ id: '123', @@ -166,6 +169,7 @@ export const sharedLinkStub = { description: null, password: null, assets: [], + slug: null, albumId: 'album-123', album: { id: 'album-123', @@ -266,6 +270,7 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, + slug: null, description: null, password: 'password', assets: [], @@ -288,6 +293,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), expired: Object.freeze({ album: undefined, @@ -303,6 +309,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), readonlyNoMetadata: Object.freeze({ id: '123', @@ -316,6 +323,7 @@ export const sharedLinkResponseStub = { allowUpload: false, allowDownload: false, showMetadata: false, + slug: null, album: { ...albumResponse, startDate: assetResponse.localDateTime, endDate: assetResponse.localDateTime }, assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }], }), diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 29031bee2e..668f624af5 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -369,7 +369,7 @@ if (sharedLink) { handleSharedLinkCreated(albumToShare); - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } return; } diff --git a/web/src/lib/components/asset-viewer/actions/download-action.svelte b/web/src/lib/components/asset-viewer/actions/download-action.svelte index e6c96da016..677550e2da 100644 --- a/web/src/lib/components/asset-viewer/actions/download-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/download-action.svelte @@ -16,7 +16,7 @@ let { asset, menuItem = false }: Props = $props(); - const onDownloadFile = async () => downloadFile(await getAssetInfo({ id: asset.id, key: authManager.key })); + const onDownloadFile = async () => downloadFile(await getAssetInfo({ ...authManager.params, id: asset.id })); diff --git a/web/src/lib/components/asset-viewer/actions/share-action.svelte b/web/src/lib/components/asset-viewer/actions/share-action.svelte index 25b3520d09..24a67848a9 100644 --- a/web/src/lib/components/asset-viewer/actions/share-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/share-action.svelte @@ -17,7 +17,7 @@ const sharedLink = await modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }); if (sharedLink) { - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } }; diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index d82b2e6532..452510f508 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -111,7 +111,7 @@ let zoomToggle = $state(() => void 0); const refreshStack = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } @@ -191,7 +191,7 @@ }); const handleGetAllAlbums = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 0ec8692180..d333d73be1 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -25,7 +25,7 @@ }; -{#if !authManager.key && $preferences?.ratings.enabled} +{#if !authManager.isSharedLink && $preferences?.ratings.enabled}
handlePromiseError(handleChangeRating(rating))} />
diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index 007e20b7c6..4dd05f520a 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -37,7 +37,7 @@ -{#if isOwner && !authManager.key} +{#if isOwner && !authManager.isSharedLink}

{$t('tags').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index ef4ddc13ce..e3c29e5c1f 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -85,7 +85,7 @@ const handleNewAsset = async (newAsset: AssetResponseDto) => { // TODO: check if reloading asset data is necessary - if (newAsset.id && !authManager.key) { + if (newAsset.id && !authManager.isSharedLink) { const data = await getAssetInfo({ id: asset.id }); people = data?.people || []; unassignedFaces = data?.unassignedFaces || []; @@ -195,7 +195,7 @@ - {#if !authManager.key && isOwner} + {#if !authManager.isSharedLink && isOwner}

{$t('people').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte index d678b00ddb..996efa7f30 100644 --- a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte +++ b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte @@ -14,7 +14,7 @@ const { asset }: Props = $props(); const loadAssetData = async (id: string) => { - const data = await viewAsset({ id, size: AssetMediaSize.Preview, key: authManager.key }); + const data = await viewAsset({ ...authManager.params, id, size: AssetMediaSize.Preview }); return URL.createObjectURL(data); }; diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index e07e5e99c6..53dce2cdf8 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -270,13 +270,13 @@ {/if} - {#if !authManager.key && asset.isFavorite} + {#if !authManager.isSharedLink && asset.isFavorite}
{/if} - {#if !authManager.key && showArchiveIcon && asset.visibility === AssetVisibility.Archive} + {#if !authManager.isSharedLink && showArchiveIcon && asset.visibility === AssetVisibility.Archive}
diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index f1a15f4429..f724c4e811 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -69,7 +69,7 @@ let paused = $state(false); let current = $state(undefined); let currentMemoryAssetFull = $derived.by(async () => - current?.asset ? await getAssetInfo({ id: current.asset.id, key: authManager.key }) : undefined, + current?.asset ? await getAssetInfo({ ...authManager.params, id: current.asset.id }) : undefined, ); let currentTimelineAssets = $derived(current?.memory.assets.map((asset) => toTimelineAsset(asset)) || []); diff --git a/web/src/lib/components/pages/SharedLinkErrorPage.svelte b/web/src/lib/components/pages/SharedLinkErrorPage.svelte new file mode 100644 index 0000000000..9103a7710c --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkErrorPage.svelte @@ -0,0 +1,14 @@ + + + + Oops! Error - Immich + + +
+

Page not found :/

+ {#if page.error?.message} +

{page.error.message}

+ {/if} +
diff --git a/web/src/lib/components/pages/SharedLinkPage.svelte b/web/src/lib/components/pages/SharedLinkPage.svelte new file mode 100644 index 0000000000..a3f5599077 --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkPage.svelte @@ -0,0 +1,108 @@ + + + + {title} + + +{#if passwordRequired} +
+
+
{$t('password_required')}
+
+ {$t('sharing_enter_password')} +
+
+
+ + + +
+
+
+
+ + {#snippet leading()} + + {/snippet} + + {#snippet trailing()} + + {/snippet} + +
+{/if} + +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} + +{/if} +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} +
+ +
+{/if} diff --git a/web/src/lib/components/photos-page/actions/create-shared-link.svelte b/web/src/lib/components/photos-page/actions/create-shared-link.svelte index e49cce24e3..40fea4acb9 100644 --- a/web/src/lib/components/photos-page/actions/create-shared-link.svelte +++ b/web/src/lib/components/photos-page/actions/create-shared-link.svelte @@ -15,7 +15,7 @@ }); if (sharedLink) { - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } }; diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte index 73f1a77742..0a1376374c 100644 --- a/web/src/lib/components/photos-page/actions/download-action.svelte +++ b/web/src/lib/components/photos-page/actions/download-action.svelte @@ -4,11 +4,11 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { downloadArchive, downloadFile } from '$lib/utils/asset-utils'; import { getAssetInfo } from '@immich/sdk'; + import { IconButton } from '@immich/ui'; import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; - import { IconButton } from '@immich/ui'; interface Props { filename?: string; @@ -23,7 +23,7 @@ const assets = [...getAssets()]; if (assets.length === 1) { clearSelect(); - let asset = await getAssetInfo({ id: assets[0].id, key: authManager.key }); + let asset = await getAssetInfo({ ...authManager.params, id: assets[0].id }); await downloadFile(asset); return; } diff --git a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte index 09ca94cb25..6e3a7c789c 100644 --- a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte +++ b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte @@ -1,15 +1,15 @@ diff --git a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte index 3e827281e7..bd1c2924b0 100644 --- a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte +++ b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte @@ -1,16 +1,16 @@ + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000..25c24ab028 --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..92d4dd042e --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,4 @@ +import { loadSharedLink } from '$lib/utils/shared-links'; +import type { PageLoad } from './$types'; + +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/routes/(user)/share/[key]/+error.svelte b/web/src/routes/(user)/share/[key]/+error.svelte index 9103a7710c..2c4d2a4d0e 100644 --- a/web/src/routes/(user)/share/[key]/+error.svelte +++ b/web/src/routes/(user)/share/[key]/+error.svelte @@ -1,14 +1,5 @@ - - Oops! Error - Immich - - -
-

Page not found :/

- {#if page.error?.message} -

{page.error.message}

- {/if} -
+ diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte index d16ba622e9..25c24ab028 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,97 +1,12 @@ - - {title} - - -{#if passwordRequired} -
-
-
{$t('password_required')}
-
- {$t('sharing_enter_password')} -
-
-
- - - -
-
-
-
- - {#snippet leading()} - - {/snippet} - - {#snippet trailing()} - - {/snippet} - -
-{/if} - -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} - -{/if} -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} -
- -
-{/if} + diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts index c0edb5e669..92d4dd042e 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -1,44 +1,4 @@ -import { getAssetThumbnailUrl, setSharedLink } from '$lib/utils'; -import { authenticate } from '$lib/utils/auth'; -import { getFormatter } from '$lib/utils/i18n'; -import { getAssetInfoFromParam } from '$lib/utils/navigation'; -import { getMySharedLink, isHttpError } from '@immich/sdk'; +import { loadSharedLink } from '$lib/utils/shared-links'; import type { PageLoad } from './$types'; -export const load = (async ({ params, url }) => { - const { key } = params; - await authenticate(url, { public: true }); - - const $t = await getFormatter(); - - try { - const [sharedLink, asset] = await Promise.all([getMySharedLink({ key }), getAssetInfoFromParam(params)]); - setSharedLink(sharedLink); - const assetCount = sharedLink.assets.length; - const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; - const assetPath = assetId ? getAssetThumbnailUrl(assetId) : '/feature-panel.png'; - - return { - sharedLink, - sharedLinkKey: key, - asset, - meta: { - title: sharedLink.album ? sharedLink.album.albumName : $t('public_share'), - description: sharedLink.description || $t('shared_photos_and_videos_count', { values: { assetCount } }), - imageUrl: assetPath, - }, - }; - } catch (error) { - if (isHttpError(error) && error.data.message === 'Invalid password') { - return { - passwordRequired: true, - sharedLinkKey: key, - meta: { - title: $t('password_required'), - }, - }; - } - - throw error; - } -}) satisfies PageLoad; +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/test-data/factories/shared-link-factory.ts b/web/src/test-data/factories/shared-link-factory.ts index a057bc936b..5768a5f9f7 100644 --- a/web/src/test-data/factories/shared-link-factory.ts +++ b/web/src/test-data/factories/shared-link-factory.ts @@ -16,4 +16,5 @@ export const sharedLinkFactory = Sync.makeFactory({ allowUpload: Sync.each(() => faker.datatype.boolean()), allowDownload: Sync.each(() => faker.datatype.boolean()), showMetadata: Sync.each(() => faker.datatype.boolean()), + slug: null, }); From e52b9d15b526ee131f14a343d0494d4f82198f72 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:34:03 +0530 Subject: [PATCH 114/169] chore: bump dart sdk to 3.8 (#20355) * chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/constants/colors.dart | 13 +- mobile/lib/constants/enums.dart | 11 +- mobile/lib/constants/filters.dart | 713 +-- mobile/lib/constants/locales.dart | 5 +- .../domain/models/asset/base_asset.model.dart | 6 +- .../models/asset/remote_asset.model.dart | 7 +- .../lib/domain/models/device_asset.model.dart | 12 +- mobile/lib/domain/models/log.model.dart | 13 +- mobile/lib/domain/models/memory.model.dart | 20 +- .../domain/models/search_result.model.dart | 15 +- mobile/lib/domain/models/setting.model.dart | 3 +- mobile/lib/domain/models/stack.model.dart | 14 +- mobile/lib/domain/models/timeline.model.dart | 14 +- mobile/lib/domain/models/user.model.dart | 39 +- .../domain/models/user_metadata.model.dart | 49 +- mobile/lib/domain/services/asset.service.dart | 6 +- mobile/lib/domain/services/hash.service.dart | 13 +- .../domain/services/local_sync.service.dart | 60 +- mobile/lib/domain/services/log.service.dart | 11 +- .../lib/domain/services/partner.service.dart | 14 +- .../domain/services/remote_album.service.dart | 33 +- .../lib/domain/services/search.service.dart | 12 +- mobile/lib/domain/services/store.service.dart | 12 +- .../domain/services/sync_stream.service.dart | 109 +- .../lib/domain/services/timeline.service.dart | 42 +- mobile/lib/domain/services/user.service.dart | 11 +- mobile/lib/domain/utils/background_sync.dart | 75 +- mobile/lib/domain/utils/event_stream.dart | 7 +- mobile/lib/entities/album.entity.dart | 9 +- mobile/lib/entities/album.entity.g.dart | 1299 +++--- .../android_device_asset.entity.g.dart | 310 +- mobile/lib/entities/asset.entity.dart | 148 +- mobile/lib/entities/asset.entity.g.dart | 2305 +++++----- mobile/lib/entities/backup_album.entity.dart | 18 +- .../lib/entities/backup_album.entity.g.dart | 374 +- .../entities/duplicated_asset.entity.g.dart | 269 +- mobile/lib/entities/etag.entity.g.dart | 419 +- .../entities/ios_device_asset.entity.g.dart | 580 ++- mobile/lib/extensions/asset_extensions.dart | 10 +- .../lib/extensions/collection_extensions.dart | 14 +- .../lib/extensions/datetime_extensions.dart | 6 +- .../maplibrecontroller_extensions.dart | 18 +- mobile/lib/extensions/scroll_extensions.dart | 22 +- mobile/lib/extensions/string_extensions.dart | 6 +- mobile/lib/extensions/theme_extensions.dart | 10 +- .../lib/extensions/translate_extensions.dart | 6 +- .../entities/asset_face.entity.drift.dart | 1086 +++-- .../entities/device_asset.entity.dart | 19 +- .../entities/device_asset.entity.g.dart | 623 ++- .../infrastructure/entities/exif.entity.dart | 118 +- .../entities/exif.entity.drift.dart | 1619 ++++--- .../entities/exif.entity.g.dart | 2084 +++++---- .../entities/local_album.entity.drift.dart | 530 ++- .../local_album_asset.entity.drift.dart | 610 +-- .../entities/local_asset.entity.dart | 26 +- .../entities/local_asset.entity.drift.dart | 818 ++-- .../infrastructure/entities/log.entity.g.dart | 913 ++-- .../entities/memory.entity.drift.dart | 1019 +++-- .../entities/memory_asset.entity.drift.dart | 573 ++- .../entities/merged_asset.drift.dart | 152 +- .../entities/partner.entity.drift.dart | 663 +-- .../entities/person.entity.drift.dart | 913 ++-- .../entities/remote_album.entity.drift.dart | 1052 +++-- .../remote_album_asset.entity.drift.dart | 611 +-- .../remote_album_user.entity.drift.dart | 689 +-- .../entities/remote_asset.entity.dart | 40 +- .../entities/remote_asset.entity.drift.dart | 1390 +++--- .../entities/stack.entity.drift.dart | 586 ++- .../entities/store.entity.g.dart | 320 +- .../infrastructure/entities/user.entity.dart | 56 +- .../entities/user.entity.drift.dart | 602 ++- .../entities/user.entity.g.dart | 979 ++-- .../entities/user_metadata.entity.drift.dart | 548 ++- .../repositories/asset_media.repository.dart | 26 +- .../repositories/backup.repository.dart | 18 +- .../repositories/db.repository.dart | 90 +- .../repositories/db.repository.drift.dart | 419 +- .../repositories/db.repository.steps.dart | 2887 +++++++----- .../repositories/exif.repository.dart | 4 +- .../repositories/local_album.repository.dart | 128 +- .../repositories/local_asset.repository.dart | 7 +- .../repositories/memory.repository.dart | 70 +- .../repositories/partner.repository.dart | 91 +- .../repositories/remote_album.repository.dart | 207 +- .../repositories/remote_asset.repository.dart | 100 +- .../repositories/search_api.repository.dart | 9 +- .../repositories/stack.repository.dart | 8 +- .../repositories/store.repository.dart | 34 +- .../repositories/sync_api.repository.dart | 10 +- .../repositories/sync_stream.repository.dart | 226 +- .../repositories/timeline.repository.dart | 401 +- .../repositories/user_api.repository.dart | 11 +- .../user_metadata.repository.dart | 20 +- .../infrastructure/utils/user.converter.dart | 100 +- mobile/lib/main.dart | 83 +- .../album_add_asset_response.model.dart | 15 +- .../lib/models/albums/album_search.model.dart | 6 +- .../albums/album_viewer_page_state.model.dart | 6 +- .../asset_selection_page_result.model.dart | 4 +- mobile/lib/models/asset_selection_state.dart | 15 +- .../models/auth/auxilary_endpoint.model.dart | 40 +- .../models/auth/biometric_status.model.dart | 10 +- .../models/backup/available_album.model.dart | 12 +- .../lib/models/backup/backup_state.model.dart | 5 +- .../backup/success_upload_asset.model.dart | 12 +- .../models/download/download_state.model.dart | 30 +- .../download/livephotos_medatada.model.dart | 30 +- .../models/folder/recursive_folder.model.dart | 6 +- .../lib/models/folder/root_folder.model.dart | 5 +- mobile/lib/models/map/map_marker.model.dart | 19 +- mobile/lib/models/memories/memory.model.dart | 15 +- .../search/search_curated_content.model.dart | 24 +- .../models/search/search_filter.model.dart | 77 +- .../models/search/search_result.model.dart | 15 +- .../server_info/server_config.model.dart | 16 +- .../server_info/server_disk_info.model.dart | 15 +- .../server_info/server_features.model.dart | 15 +- .../server_info/server_version.model.dart | 23 +- .../models/shared_link/shared_link.model.dart | 34 +- .../upload/share_intent_attachment.model.dart | 21 +- ...additional_shared_user_selection.page.dart | 72 +- .../album/album_asset_selection.page.dart | 20 +- .../lib/pages/album/album_control_button.dart | 6 +- mobile/lib/pages/album/album_date_range.dart | 9 +- mobile/lib/pages/album/album_description.dart | 5 +- .../lib/pages/album/album_options.page.dart | 49 +- .../pages/album/album_shared_user_icons.dart | 6 +- .../album_shared_user_selection.page.dart | 63 +- mobile/lib/pages/album/album_title.dart | 18 +- mobile/lib/pages/album/album_viewer.dart | 19 +- mobile/lib/pages/album/album_viewer.page.dart | 4 +- mobile/lib/pages/albums/albums.page.dart | 161 +- .../lib/pages/backup/album_preview.page.dart | 27 +- .../backup/backup_album_selection.page.dart | 120 +- .../pages/backup/backup_controller.page.dart | 148 +- .../lib/pages/backup/backup_options.page.dart | 4 +- .../lib/pages/backup/drift_backup.page.dart | 68 +- .../drift_backup_album_selection.page.dart | 143 +- .../backup/drift_upload_detail.page.dart | 172 +- .../backup/failed_backup_status.page.dart | 42 +- mobile/lib/pages/common/activities.page.dart | 13 +- mobile/lib/pages/common/app_log.page.dart | 70 +- .../lib/pages/common/app_log_detail.page.dart | 52 +- .../pages/common/change_experience.page.dart | 30 +- .../lib/pages/common/create_album.page.dart | 68 +- mobile/lib/pages/common/download_panel.dart | 58 +- .../common/gallery_stacked_children.dart | 10 +- .../lib/pages/common/gallery_viewer.page.dart | 102 +- .../pages/common/headers_settings.page.dart | 19 +- .../lib/pages/common/large_leading_tile.dart | 15 +- .../common/native_video_viewer.page.dart | 122 +- mobile/lib/pages/common/settings.page.dart | 89 +- .../lib/pages/common/splash_screen.page.dart | 27 +- .../lib/pages/common/tab_controller.page.dart | 60 +- mobile/lib/pages/common/tab_shell.page.dart | 66 +- mobile/lib/pages/editing/crop.page.dart | 55 +- mobile/lib/pages/editing/edit.page.dart | 111 +- mobile/lib/pages/editing/filter.page.dart | 55 +- mobile/lib/pages/library/archive.page.dart | 9 +- mobile/lib/pages/library/favorite.page.dart | 9 +- .../lib/pages/library/folder/folder.page.dart | 136 +- mobile/lib/pages/library/library.page.dart | 128 +- .../lib/pages/library/local_albums.page.dart | 27 +- .../lib/pages/library/locked/locked.page.dart | 45 +- .../pages/library/locked/pin_auth.page.dart | 30 +- .../library/partner/drift_partner.page.dart | 33 +- .../pages/library/partner/partner.page.dart | 34 +- .../library/partner/partner_detail.page.dart | 49 +- .../people/people_collection.page.dart | 25 +- .../places/places_collection.page.dart | 27 +- .../library/shared_link/shared_link.page.dart | 44 +- .../shared_link/shared_link_edit.page.dart | 191 +- mobile/lib/pages/library/trash.page.dart | 72 +- .../lib/pages/login/change_password.page.dart | 4 +- mobile/lib/pages/login/login.page.dart | 10 +- .../permission_onboarding.page.dart | 70 +- mobile/lib/pages/photos/memory.page.dart | 72 +- mobile/lib/pages/photos/photos.page.dart | 21 +- .../pages/search/all_motion_videos.page.dart | 11 +- mobile/lib/pages/search/all_people.page.dart | 9 +- mobile/lib/pages/search/all_places.page.dart | 15 +- mobile/lib/pages/search/all_videos.page.dart | 5 +- mobile/lib/pages/search/map/map.page.dart | 76 +- .../search/map/map_location_picker.page.dart | 30 +- .../lib/pages/search/person_result.page.dart | 58 +- .../lib/pages/search/recently_taken.page.dart | 11 +- mobile/lib/pages/search/search.page.dart | 272 +- .../settings/beta_sync_settings.page.dart | 8 +- .../pages/share_intent/share_intent.page.dart | 147 +- mobile/lib/platform/native_sync_api.g.dart | 44 +- .../pages/dev/feat_in_development.page.dart | 73 +- .../pages/dev/media_stat.page.dart | 97 +- .../presentation/pages/drift_album.page.dart | 13 +- .../pages/drift_archive.page.dart | 20 +- .../drift_asset_selection_timeline.page.dart | 33 +- .../pages/drift_create_album.page.dart | 152 +- .../pages/drift_favorite.page.dart | 20 +- .../pages/drift_library.page.dart | 137 +- .../pages/drift_local_album.page.dart | 51 +- .../pages/drift_locked_folder.page.dart | 24 +- .../presentation/pages/drift_memory.page.dart | 86 +- .../pages/drift_partner_detail.page.dart | 54 +- .../presentation/pages/drift_place.page.dart | 40 +- .../pages/drift_place_detail.page.dart | 22 +- .../pages/drift_recently_taken.page.dart | 26 +- .../pages/drift_remote_album.page.dart | 127 +- .../presentation/pages/drift_trash.page.dart | 28 +- .../pages/drift_user_selection.page.dart | 68 +- .../presentation/pages/drift_video.page.dart | 24 +- .../pages/local_timeline.page.dart | 12 +- .../pages/search/drift_search.page.dart | 287 +- .../search/paginated_search.provider.dart | 5 +- .../archive_action_button.widget.dart | 5 +- .../base_action_button.widget.dart | 13 +- .../cast_action_button.widget.dart | 5 +- .../delete_action_button.widget.dart | 9 +- .../delete_local_action_button.widget.dart | 5 +- .../delete_trash_action_button.widget.dart | 11 +- .../edit_location_action_button.widget.dart | 5 +- .../favorite_action_button.widget.dart | 11 +- ...emove_from_album_action_button.widget.dart | 6 +- .../restore_trash_action_button.widget.dart | 17 +- .../stack_action_button.widget.dart | 5 +- .../trash_action_button.widget.dart | 5 +- .../unarchive_action_button.widget.dart | 5 +- .../unfavorite_action_button.widget.dart | 11 +- .../unstack_action_button.widget.dart | 5 +- .../upload_action_button.widget.dart | 5 +- .../widgets/album/album_selector.widget.dart | 309 +- .../asset_viewer/asset_stack.provider.dart | 6 +- .../asset_viewer/asset_stack.widget.dart | 34 +- .../asset_viewer/asset_viewer.page.dart | 86 +- .../asset_viewer/asset_viewer.state.dart | 10 +- .../asset_viewer/bottom_bar.widget.dart | 26 +- .../asset_viewer/bottom_sheet.widget.dart | 32 +- .../bottom_sheet/location_details.widget.dart | 37 +- .../asset_viewer/top_app_bar.widget.dart | 27 +- .../asset_viewer/video_viewer.widget.dart | 123 +- .../video_viewer_controls.widget.dart | 38 +- .../backup/backup_toggle_button.widget.dart | 72 +- .../archive_bottom_sheet.widget.dart | 12 +- .../base_bottom_sheet.widget.dart | 10 +- .../favorite_bottom_sheet.widget.dart | 12 +- .../general_bottom_sheet.widget.dart | 31 +- .../remote_album_bottom_sheet.widget.dart | 17 +- .../widgets/images/image_provider.dart | 30 +- .../images/local_album_thumbnail.widget.dart | 26 +- .../widgets/images/local_image_provider.dart | 46 +- .../widgets/images/remote_image_provider.dart | 24 +- .../widgets/images/thumb_hash_provider.dart | 19 +- .../widgets/images/thumbnail.widget.dart | 36 +- .../widgets/images/thumbnail_tile.widget.dart | 116 +- .../memory/memory_bottom_info.widget.dart | 72 +- .../widgets/memory/memory_card.widget.dart | 46 +- .../widgets/memory/memory_lane.widget.dart | 47 +- .../drift_album_option.widget.dart | 48 +- .../widgets/timeline/fixed/row.dart | 12 +- .../widgets/timeline/fixed/segment.model.dart | 53 +- .../timeline/fixed/segment_builder.dart | 3 +- .../widgets/timeline/header.widget.dart | 51 +- .../widgets/timeline/scrubber.widget.dart | 158 +- .../widgets/timeline/segment.model.dart | 4 +- .../widgets/timeline/segment_builder.dart | 36 +- .../widgets/timeline/timeline.state.dart | 58 +- .../widgets/timeline/timeline.widget.dart | 143 +- mobile/lib/providers/activity.provider.dart | 9 +- mobile/lib/providers/activity.provider.g.dart | 77 +- .../activity_statistics.provider.g.dart | 65 +- .../lib/providers/album/album.provider.dart | 34 +- .../album/album_sort_by_options.provider.dart | 28 +- .../album_sort_by_options.provider.g.dart | 32 +- .../providers/album/album_title.provider.dart | 4 +- .../album/album_viewer.provider.dart | 29 +- .../album/current_album.provider.g.dart | 15 +- mobile/lib/providers/api.provider.g.dart | 5 +- .../providers/app_life_cycle.provider.dart | 31 +- mobile/lib/providers/asset.provider.dart | 29 +- .../asset_viewer/asset_people.provider.g.dart | 77 +- .../current_asset.provider.g.dart | 15 +- .../asset_viewer/download.provider.dart | 59 +- .../share_intent_upload.provider.dart | 27 +- .../video_player_controls_provider.dart | 14 +- .../video_player_value_provider.dart | 40 +- mobile/lib/providers/auth.provider.dart | 44 +- .../lib/providers/backup/backup.provider.dart | 172 +- .../backup/backup_album.provider.dart | 4 +- .../backup/backup_verification.provider.dart | 16 +- .../backup_verification.provider.g.dart | 16 +- .../backup/drift_backup.provider.dart | 88 +- .../backup/manual_upload.provider.dart | 95 +- mobile/lib/providers/cast.provider.dart | 22 +- mobile/lib/providers/folder.provider.dart | 11 +- .../gallery_permission.provider.dart | 5 +- .../providers/image/cache/image_loader.dart | 11 +- .../cache/remote_image_cache_manager.dart | 9 +- .../cache/thumbnail_image_cache_manager.dart | 9 +- .../image/immich_local_image_provider.dart | 12 +- .../immich_local_thumbnail_provider.dart | 24 +- .../image/immich_remote_image_provider.dart | 38 +- .../immich_remote_thumbnail_provider.dart | 38 +- .../lib/providers/immich_logo_provider.g.dart | 5 +- .../infrastructure/action.provider.dart | 148 +- .../infrastructure/album.provider.dart | 5 +- .../asset_viewer/current_asset.provider.dart | 20 +- .../infrastructure/db.provider.g.dart | 5 +- .../infrastructure/person.provider.dart | 4 +- .../infrastructure/remote_album.provider.dart | 102 +- .../infrastructure/search.provider.dart | 8 +- .../infrastructure/stack.provider.dart | 4 +- .../infrastructure/storage.provider.dart | 4 +- .../infrastructure/store.provider.g.dart | 5 +- .../infrastructure/sync.provider.dart | 8 +- .../infrastructure/timeline.provider.dart | 16 +- .../infrastructure/user.provider.dart | 17 +- .../infrastructure/user.provider.g.dart | 5 +- mobile/lib/providers/local_auth.provider.dart | 22 +- .../providers/map/map_marker.provider.g.dart | 5 +- .../providers/map/map_service.provider.g.dart | 5 +- .../lib/providers/map/map_state.provider.dart | 45 +- .../providers/map/map_state.provider.g.dart | 16 +- mobile/lib/providers/network.provider.dart | 4 +- .../notification_permission.provider.dart | 4 +- mobile/lib/providers/partner.provider.dart | 54 +- .../search/paginated_search.provider.dart | 14 +- .../search/paginated_search.provider.g.dart | 16 +- .../lib/providers/search/people.provider.dart | 10 +- .../providers/search/people.provider.g.dart | 116 +- .../search/search_filter.provider.g.dart | 46 +- .../search/search_page_state.provider.dart | 16 +- .../lib/providers/server_info.provider.dart | 85 +- .../lib/providers/shared_link.provider.dart | 4 +- .../lib/providers/sync_status.provider.dart | 28 +- mobile/lib/providers/tab.provider.dart | 4 +- mobile/lib/providers/theme.provider.dart | 9 +- mobile/lib/providers/timeline.provider.dart | 37 +- .../timeline/multiselect.provider.dart | 63 +- mobile/lib/providers/trash.provider.dart | 8 +- .../upload_profile_image.provider.dart | 34 +- mobile/lib/providers/websocket.provider.dart | 79 +- .../repositories/activity_api.repository.dart | 21 +- mobile/lib/repositories/album.repository.dart | 24 +- .../repositories/album_api.repository.dart | 53 +- .../repositories/album_media.repository.dart | 48 +- mobile/lib/repositories/asset.repository.dart | 78 +- .../repositories/asset_api.repository.dart | 66 +- .../repositories/asset_media.repository.dart | 8 +- .../lib/repositories/auth_api.repository.dart | 13 +- .../repositories/biometric.repository.dart | 9 +- .../lib/repositories/download.repository.dart | 5 +- .../drift_album_api_repository.dart | 50 +- .../repositories/file_media.repository.dart | 49 +- .../repositories/folder_api.repository.dart | 6 +- mobile/lib/repositories/gcast.repository.dart | 10 +- .../lib/repositories/partner.repository.dart | 14 +- .../repositories/partner_api.repository.dart | 22 +- .../repositories/person_api.repository.dart | 20 +- .../repositories/sessions_api.repository.dart | 18 +- .../share_handler.repository.dart | 8 +- .../lib/repositories/timeline.repository.dart | 25 +- .../lib/repositories/upload.repository.dart | 25 +- .../lib/repositories/widget.repository.dart | 5 +- .../lib/routing/app_navigation_observer.dart | 29 +- mobile/lib/routing/duplicate_guard.dart | 4 +- mobile/lib/routing/locked_guard.dart | 10 +- mobile/lib/routing/router.dart | 289 +- mobile/lib/routing/router.gr.dart | 549 +-- mobile/lib/services/action.service.dart | 76 +- mobile/lib/services/activity.service.dart | 19 +- mobile/lib/services/album.service.dart | 143 +- mobile/lib/services/api.service.dart | 16 +- mobile/lib/services/app_settings.service.dart | 69 +- mobile/lib/services/asset.service.dart | 133 +- mobile/lib/services/auth.service.dart | 5 +- mobile/lib/services/background.service.dart | 105 +- mobile/lib/services/backup.service.dart | 106 +- .../services/backup_verification.service.dart | 83 +- mobile/lib/services/deep_link.service.dart | 18 +- mobile/lib/services/download.service.dart | 49 +- mobile/lib/services/entity.service.dart | 10 +- mobile/lib/services/folder.service.dart | 34 +- mobile/lib/services/gcast.service.dart | 47 +- mobile/lib/services/hash.service.dart | 25 +- mobile/lib/services/local_auth.service.dart | 6 +- .../services/local_files_manager.service.dart | 9 +- .../services/local_notification.service.dart | 34 +- mobile/lib/services/memory.service.dart | 29 +- mobile/lib/services/network.service.dart | 5 +- mobile/lib/services/oauth.service.dart | 38 +- mobile/lib/services/partner.service.dart | 16 +- mobile/lib/services/person.service.dart | 14 +- mobile/lib/services/search.service.dart | 6 +- .../lib/services/secure_storage.service.dart | 4 +- mobile/lib/services/server_info.service.dart | 6 +- mobile/lib/services/share.service.dart | 14 +- mobile/lib/services/share_intent_service.dart | 10 +- mobile/lib/services/shared_link.service.dart | 4 +- mobile/lib/services/stack.service.dart | 19 +- mobile/lib/services/sync.service.dart | 251 +- mobile/lib/services/timeline.service.dart | 31 +- mobile/lib/services/trash.service.dart | 20 +- mobile/lib/services/upload.service.dart | 58 +- mobile/lib/services/widget.service.dart | 4 +- mobile/lib/theme/color_scheme.dart | 45 +- mobile/lib/theme/dynamic_theme.dart | 10 +- mobile/lib/theme/theme_data.dart | 111 +- mobile/lib/utils/backup_progress.dart | 6 +- mobile/lib/utils/bootstrap.dart | 5 +- .../lib/utils/cache/custom_image_cache.dart | 6 +- mobile/lib/utils/color_filter_generator.dart | 8 +- mobile/lib/utils/debounce.dart | 20 +- .../utils/draggable_scroll_controller.dart | 14 +- .../utils/hooks/app_settings_update_hook.dart | 9 +- mobile/lib/utils/hooks/blurhash_hook.dart | 8 +- .../lib/utils/hooks/crop_controller_hook.dart | 6 +- mobile/lib/utils/hooks/interval_hook.dart | 11 +- mobile/lib/utils/hooks/timer_hook.dart | 17 +- mobile/lib/utils/http_ssl_cert_override.dart | 6 +- mobile/lib/utils/http_ssl_options.dart | 15 +- mobile/lib/utils/image_url_builder.dart | 40 +- mobile/lib/utils/immich_loading_overlay.dart | 5 +- mobile/lib/utils/isolate.dart | 10 +- mobile/lib/utils/map_utils.dart | 62 +- mobile/lib/utils/migration.dart | 83 +- mobile/lib/utils/openapi_patching.dart | 12 +- mobile/lib/utils/remote_album.utils.dart | 45 +- mobile/lib/utils/selection_handlers.dart | 70 +- mobile/lib/utils/throttle.dart | 10 +- mobile/lib/utils/thumbnail_utils.dart | 11 +- mobile/lib/utils/url_helper.dart | 34 +- mobile/lib/utils/version_compatibility.dart | 7 +- .../activities/activity_text_field.dart | 34 +- .../lib/widgets/activities/activity_tile.dart | 27 +- .../activities/dismissible_activity.dart | 21 +- .../album/add_to_album_bottom_sheet.dart | 62 +- .../album/add_to_album_sliverlist.dart | 11 +- .../album/album_action_filled_button.dart | 27 +- .../widgets/album/album_thumbnail_card.dart | 46 +- .../album/album_thumbnail_listtile.dart | 51 +- .../widgets/album/album_title_text_field.dart | 19 +- .../widgets/album/album_viewer_appbar.dart | 60 +- .../album_viewer_editable_description.dart | 34 +- .../album/album_viewer_editable_title.dart | 47 +- .../album/remote_album_shared_user_icons.dart | 11 +- .../album/shared_album_thumbnail_image.dart | 10 +- .../widgets/asset_grid/asset_drag_region.dart | 16 +- .../asset_grid/asset_grid_data_structure.dart | 60 +- .../asset_grid/control_bottom_app_bar.dart | 110 +- .../lib/widgets/asset_grid/delete_dialog.dart | 38 +- .../disable_multi_select_button.dart | 16 +- .../asset_grid/draggable_scrollbar.dart | 160 +- .../draggable_scrollbar_custom.dart | 117 +- .../asset_grid/group_divider_title.dart | 15 +- .../widgets/asset_grid/immich_asset_grid.dart | 37 +- .../asset_grid/immich_asset_grid_view.dart | 107 +- .../widgets/asset_grid/multiselect_grid.dart | 165 +- .../multiselect_grid_status_indicator.dart | 19 +- .../widgets/asset_grid/thumbnail_image.dart | 171 +- .../asset_grid/thumbnail_placeholder.dart | 13 +- .../lib/widgets/asset_grid/upload_dialog.dart | 14 +- .../asset_viewer/advanced_bottom_sheet.dart | 40 +- .../asset_viewer/animated_play_pause.dart | 7 +- .../asset_viewer/bottom_gallery_bar.dart | 69 +- .../lib/widgets/asset_viewer/cast_dialog.dart | 44 +- .../asset_viewer/center_play_button.dart | 10 +- .../custom_video_player_controls.dart | 38 +- .../asset_viewer/description_input.dart | 39 +- .../detail_panel/asset_date_time.dart | 14 +- .../detail_panel/asset_details.dart | 6 +- .../detail_panel/asset_location.dart | 30 +- .../detail_panel/camera_info.dart | 15 +- .../asset_viewer/detail_panel/exif_map.dart | 23 +- .../asset_viewer/detail_panel/file_info.dart | 15 +- .../detail_panel/people_info.dart | 19 +- .../asset_viewer/formatted_duration.dart | 6 +- .../widgets/asset_viewer/gallery_app_bar.dart | 16 +- .../asset_viewer/top_control_app_bar.dart | 56 +- .../widgets/asset_viewer/video_controls.dart | 5 +- .../widgets/asset_viewer/video_position.dart | 23 +- .../lib/widgets/backup/album_info_card.dart | 53 +- .../widgets/backup/album_info_list_tile.dart | 33 +- .../lib/widgets/backup/asset_info_table.dart | 51 +- .../lib/widgets/backup/backup_info_card.dart | 31 +- .../backup/current_backup_asset_info_box.dart | 11 +- .../backup/drift_album_info_list_tile.dart | 29 +- mobile/lib/widgets/backup/error_chip.dart | 5 +- .../lib/widgets/backup/error_chip_text.dart | 6 +- .../backup/icloud_download_progress_bar.dart | 25 +- .../widgets/backup/ios_debug_info_tile.dart | 28 +- .../widgets/backup/upload_progress_bar.dart | 29 +- mobile/lib/widgets/backup/upload_stats.dart | 21 +- .../common/app_bar_dialog/app_bar_dialog.dart | 120 +- .../app_bar_dialog/app_bar_profile_info.dart | 50 +- .../app_bar_dialog/app_bar_server_info.dart | 57 +- mobile/lib/widgets/common/confirm_dialog.dart | 14 +- .../lib/widgets/common/date_time_picker.dart | 94 +- .../common/delayed_loading_indicator.dart | 17 +- mobile/lib/widgets/common/drag_sheet.dart | 12 +- .../widgets/common/dropdown_search_menu.dart | 37 +- .../common/fade_in_placeholder_image.dart | 7 +- mobile/lib/widgets/common/immich_app_bar.dart | 72 +- mobile/lib/widgets/common/immich_image.dart | 37 +- .../common/immich_loading_indicator.dart | 32 +- mobile/lib/widgets/common/immich_logo.dart | 6 +- .../widgets/common/immich_sliver_app_bar.dart | 104 +- .../lib/widgets/common/immich_thumbnail.dart | 48 +- .../lib/widgets/common/immich_title_text.dart | 10 +- mobile/lib/widgets/common/immich_toast.dart | 40 +- .../common/local_album_sliver_app_bar.dart | 8 +- .../lib/widgets/common/location_picker.dart | 48 +- .../common/mesmerizing_sliver_app_bar.dart | 144 +- .../common/remote_album_sliver_app_bar.dart | 192 +- .../widgets/common/scaffold_error_body.dart | 12 +- mobile/lib/widgets/common/search_field.dart | 36 +- .../common/selection_sliver_app_bar.dart | 29 +- mobile/lib/widgets/common/share_dialog.dart | 5 +- .../widgets/common/thumbhash_placeholder.dart | 11 +- .../widgets/common/user_circle_avatar.dart | 15 +- .../widgets/forms/change_password_form.dart | 40 +- .../lib/widgets/forms/login/email_input.dart | 12 +- .../lib/widgets/forms/login/loading_icon.dart | 10 +- .../lib/widgets/forms/login/login_button.dart | 14 +- .../lib/widgets/forms/login/login_form.dart | 110 +- .../forms/login/o_auth_login_button.dart | 5 +- .../widgets/forms/login/password_input.dart | 16 +- .../forms/login/server_endpoint_input.dart | 7 +- mobile/lib/widgets/forms/pin_input.dart | 34 +- .../widgets/forms/pin_registration_form.dart | 32 +- .../widgets/forms/pin_verification_form.dart | 10 +- mobile/lib/widgets/map/map_app_bar.dart | 56 +- mobile/lib/widgets/map/map_asset_grid.dart | 86 +- mobile/lib/widgets/map/map_bottom_sheet.dart | 10 +- .../map_settings/map_settings_list_tile.dart | 12 +- .../map_settings_time_dropdown.dart | 57 +- .../map/map_settings/map_theme_picker.dart | 15 +- .../lib/widgets/map/map_theme_override.dart | 6 +- mobile/lib/widgets/map/map_thumbnail.dart | 6 +- .../map/positioned_asset_marker_icon.dart | 50 +- .../widgets/memories/memory_bottom_info.dart | 62 +- mobile/lib/widgets/memories/memory_card.dart | 57 +- .../lib/widgets/memories/memory_epilogue.dart | 38 +- mobile/lib/widgets/memories/memory_lane.dart | 48 +- .../memories/memory_progress_indicator.dart | 15 +- mobile/lib/widgets/photo_view/photo_view.dart | 100 +- .../photo_view/photo_view_gallery.dart | 39 +- .../src/controller/photo_view_controller.dart | 35 +- .../photo_view_controller_delegate.dart | 48 +- .../photo_view_scalestate_controller.dart | 5 +- .../photo_view/src/core/photo_view_core.dart | 80 +- .../src/core/photo_view_gesture_detector.dart | 35 +- .../src/core/photo_view_hit_corners.dart | 5 +- .../src/photo_view_default_widgets.dart | 14 +- .../photo_view/src/photo_view_wrappers.dart | 27 +- .../src/utils/ignorable_change_notifier.dart | 6 +- .../src/utils/photo_view_utils.dart | 25 +- .../widgets/search/curated_people_row.dart | 34 +- .../widgets/search/curated_places_row.dart | 4 +- mobile/lib/widgets/search/explore_grid.dart | 28 +- .../widgets/search/person_name_edit_form.dart | 29 +- .../search/search_filter/camera_picker.dart | 49 +- .../search/search_filter/common/dropdown.dart | 4 +- .../search_filter/display_option_picker.dart | 27 +- .../filter_bottom_sheet_scaffold.dart | 5 +- .../search/search_filter/location_picker.dart | 53 +- .../search/search_filter/people_picker.dart | 18 +- .../search_filter/search_filter_chip.dart | 38 +- .../widgets/search/search_map_thumbnail.dart | 16 +- .../widgets/search/search_row_section.dart | 5 +- .../lib/widgets/search/search_row_title.dart | 17 +- .../widgets/search/thumbnail_with_info.dart | 7 +- .../search/thumbnail_with_info_container.dart | 11 +- .../widgets/settings/advanced_settings.dart | 5 +- .../asset_list_group_settings.dart | 24 +- .../asset_list_layout_settings.dart | 4 +- .../asset_list_settings.dart | 9 +- .../asset_viewer_settings.dart | 14 +- .../image_viewer_quality_setting.dart | 9 +- .../video_viewer_settings.dart | 4 +- .../backup_settings/background_settings.dart | 103 +- .../backup_settings/backup_settings.dart | 18 +- .../beta_sync_settings.dart | 160 +- .../beta_sync_settings/entity_count_tile.dart | 37 +- .../settings/beta_timeline_list_tile.dart | 91 +- .../custome_proxy_headers_settings.dart | 8 +- .../widgets/settings/language_settings.dart | 104 +- .../settings/local_storage_settings.dart | 19 +- .../networking_settings/endpoint_input.dart | 19 +- .../external_network_preference.dart | 62 +- .../local_network_preference.dart | 88 +- .../networking_settings.dart | 129 +- .../settings/notification_setting.dart | 22 +- .../preference_settings/haptic_setting.dart | 4 +- .../preference_setting.dart | 14 +- .../primary_color_setting.dart | 43 +- .../preference_settings/theme_setting.dart | 4 +- .../settings/settings_button_list_tile.dart | 11 +- .../lib/widgets/settings/settings_card.dart | 22 +- .../settings/settings_radio_list_tile.dart | 14 +- .../settings/settings_slider_list_tile.dart | 7 +- .../settings/settings_sub_page_scaffold.dart | 12 +- .../widgets/settings/settings_sub_title.dart | 10 +- .../settings/settings_switch_list_tile.dart | 13 +- .../settings/ssl_client_cert_settings.dart | 54 +- .../widgets/shared_link/shared_link_item.dart | 72 +- mobile/pubspec.lock | 2 +- mobile/pubspec.yaml | 2 +- .../domain/services/hash_service_test.dart | 15 +- .../domain/services/log_service_test.dart | 33 +- .../domain/services/store_service_test.dart | 20 +- .../services/sync_stream_service_test.dart | 157 +- .../domain/services/user_service_test.dart | 18 +- .../test/drift/main/generated/schema_v1.dart | 3698 ++++++++++----- .../test/drift/main/generated/schema_v2.dart | 3698 ++++++++++----- .../test/drift/main/generated/schema_v3.dart | 3693 ++++++++++----- .../test/drift/main/generated/schema_v4.dart | 4002 +++++++++++------ mobile/test/fixtures/album.stub.dart | 29 +- mobile/test/fixtures/sync_stream.stub.dart | 32 +- .../local_album_repository_test.dart | 39 +- .../repositories/store_repository_test.dart | 31 +- .../sync_api_repository_test.dart | 174 +- mobile/test/mock_http_override.dart | 9 +- .../activity/activities_page_test.dart | 199 +- .../activity/activity_provider_test.dart | 123 +- .../activity_statistics_provider_test.dart | 33 +- .../activity/activity_text_field_test.dart | 91 +- .../modules/activity/activity_tile_test.dart | 84 +- .../activity/dismissible_activity_test.dart | 28 +- .../album_sort_by_options_provider_test.dart | 184 +- .../extensions/asset_extensions_test.dart | 21 +- .../extensions/builtin_extensions_test.dart | 9 +- .../home/asset_grid_data_structure_test.dart | 30 +- .../modules/map/map_theme_override_test.dart | 9 +- .../modules/shared/sync_service_test.dart | 79 +- .../test/modules/utils/async_mutex_test.dart | 28 +- .../modules/utils/thumbnail_utils_test.dart | 21 +- .../utils/version_compatibility_test.dart | 5 +- .../test/pages/search/search.page_test.dart | 40 +- mobile/test/services/album.service_test.dart | 90 +- mobile/test/services/asset.service_test.dart | 8 +- mobile/test/services/auth.service_test.dart | 84 +- mobile/test/services/entity.service_test.dart | 54 +- mobile/test/services/hash_service_test.dart | 100 +- mobile/test/test_utils.dart | 27 +- mobile/test/widget_tester_extensions.dart | 5 +- 643 files changed, 32561 insertions(+), 35292 deletions(-) diff --git a/mobile/lib/constants/colors.dart b/mobile/lib/constants/colors.dart index 1614a308e0..069ed519cf 100644 --- a/mobile/lib/constants/colors.dart +++ b/mobile/lib/constants/colors.dart @@ -1,17 +1,6 @@ import 'package:flutter/material.dart'; -enum ImmichColorPreset { - indigo, - deepPurple, - pink, - red, - orange, - yellow, - lime, - green, - cyan, - slateGray, -} +enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray } const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo; const String defaultColorPresetName = "indigo"; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index febc71032e..6ec0ce37ea 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -1,13 +1,6 @@ -enum SortOrder { - asc, - desc, -} +enum SortOrder { asc, desc } -enum TextSearchType { - context, - filename, - description, -} +enum TextSearchType { context, filename, description } enum AssetVisibilityEnum { timeline, hidden, archive, locked } diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart index 61597f08d1..e77bcfbf1f 100644 --- a/mobile/lib/constants/filters.dart +++ b/mobile/lib/constants/filters.dart @@ -2,511 +2,49 @@ import 'package:flutter/material.dart'; const List filters = [ //Original - ColorFilter.matrix([ - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), //Vintage - ColorFilter.matrix([ - 0.8, - 0.1, - 0.1, - 0, - 20, - 0.1, - 0.8, - 0.1, - 0, - 20, - 0.1, - 0.1, - 0.8, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), //Mood - ColorFilter.matrix([ - 1.2, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), //Crisp - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Cool - ColorFilter.matrix([ - 0.9, - 0, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Blush - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), //Sunkissed - ColorFilter.matrix([ - 1.3, - 0, - 0.1, - 0, - 15, - 0, - 1.1, - 0.1, - 0, - 10, - 0, - 0, - 0.9, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), //Fresh - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 20, - 0, - 1.2, - 0, - 0, - 20, - 0, - 0, - 1.1, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), //Classic - ColorFilter.matrix([ - 1.1, - 0, - -0.1, - 0, - 10, - -0.1, - 1.1, - 0.1, - 0, - 5, - 0, - -0.1, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Lomo-ish - ColorFilter.matrix([ - 1.5, - 0, - 0.1, - 0, - 0, - 0, - 1.45, - 0, - 0, - 0, - 0.1, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Nashville - ColorFilter.matrix([ - 1.2, - 0.15, - -0.15, - 0, - 15, - 0.1, - 1.1, - 0.1, - 0, - 10, - -0.05, - 0.2, - 1.25, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), //Valencia - ColorFilter.matrix([ - 1.15, - 0.1, - 0.1, - 0, - 20, - 0.1, - 1.1, - 0, - 0, - 10, - 0.1, - 0.1, - 1.2, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), //Clarendon - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 10, - 0, - 1.25, - 0, - 0, - 10, - 0, - 0, - 1.3, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), //Moon - ColorFilter.matrix([ - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), //Willow - ColorFilter.matrix([ - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), //Kodak - ColorFilter.matrix([ - 1.3, - 0.1, - -0.1, - 0, - 10, - 0, - 1.25, - 0.1, - 0, - 10, - 0, - -0.1, - 1.1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), //Frost - ColorFilter.matrix([ - 0.8, - 0.2, - 0.1, - 0, - 0, - 0.2, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), //Night Vision - ColorFilter.matrix([ - 0.1, - 0.95, - 0.2, - 0, - 0, - 0.1, - 1.5, - 0.1, - 0, - 0, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), //Sunset - ColorFilter.matrix([ - 1.5, - 0.2, - 0, - 0, - 0, - 0.1, - 0.9, - 0.1, - 0, - 0, - -0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Noir - ColorFilter.matrix([ - 1.3, - -0.3, - 0.1, - 0, - 0, - -0.1, - 1.2, - -0.1, - 0, - 0, - 0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Dreamy - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 0, - 0.1, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.1, - 0, - 15, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), //Sepia - ColorFilter.matrix([ - 0.393, - 0.769, - 0.189, - 0, - 0, - 0.349, - 0.686, - 0.168, - 0, - 0, - 0.272, - 0.534, - 0.131, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]), //Radium ColorFilter.matrix([ 1.438, @@ -554,212 +92,23 @@ const List filters = [ 0, ]), //Purple Haze - ColorFilter.matrix([ - 1.3, - 0, - 1.2, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0.2, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Lemonade - ColorFilter.matrix([ - 1.2, - 0.1, - 0, - 0, - 0, - 0, - 1.1, - 0.2, - 0, - 0, - 0.1, - 0, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), //Caramel - ColorFilter.matrix([ - 1.6, - 0.2, - 0, - 0, - 0, - 0.1, - 1.3, - 0.1, - 0, - 0, - 0, - 0.1, - 0.9, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), //Peachy - ColorFilter.matrix([ - 1.3, - 0.5, - 0, - 0, - 0, - 0.2, - 1.1, - 0.3, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Neon - ColorFilter.matrix([ - 1, - 0, - 1, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), //Cold Morning - ColorFilter.matrix([ - 0.9, - 0.1, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Lush - ColorFilter.matrix([ - 0.9, - 0.2, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Urban Neon - ColorFilter.matrix([ - 1.1, - 0, - 0.3, - 0, - 0, - 0, - 0.9, - 0.3, - 0, - 0, - 0.3, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Monochrome - ColorFilter.matrix([ - 0.6, - 0.2, - 0.2, - 0, - 0, - 0.2, - 0.6, - 0.2, - 0, - 0, - 0.2, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), ]; const List filterNames = [ diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index 601a83b563..f3c24384b0 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -51,7 +51,4 @@ const Map locales = { const String translationsPath = 'assets/i18n'; -const List localesNotSupportedByOverpass = [ - Locale('el', 'GR'), - Locale('sr', 'Cyrl'), -]; +const List localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')]; diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index 7cd4caab6a..4d40be2d32 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -9,11 +9,7 @@ enum AssetType { audio, } -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } sealed class BaseAsset { final String name; diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index db3e53cd2e..8648255167 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -1,11 +1,6 @@ part of 'base_asset.model.dart'; -enum AssetVisibility { - timeline, - hidden, - archive, - locked, -} +enum AssetVisibility { timeline, hidden, archive, locked } // Model for an asset stored in the server class RemoteAsset extends BaseAsset { diff --git a/mobile/lib/domain/models/device_asset.model.dart b/mobile/lib/domain/models/device_asset.model.dart index b0949ccc96..a404f5a9e2 100644 --- a/mobile/lib/domain/models/device_asset.model.dart +++ b/mobile/lib/domain/models/device_asset.model.dart @@ -5,11 +5,7 @@ class DeviceAsset { final Uint8List hash; final DateTime modifiedTime; - const DeviceAsset({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime}); @override bool operator ==(covariant DeviceAsset other) { @@ -28,11 +24,7 @@ class DeviceAsset { return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)'; } - DeviceAsset copyWith({ - String? assetId, - Uint8List? hash, - DateTime? modifiedTime, - }) { + DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) { return DeviceAsset( assetId: assetId ?? this.assetId, hash: hash ?? this.hash, diff --git a/mobile/lib/domain/models/log.model.dart b/mobile/lib/domain/models/log.model.dart index f58cae8063..9902ca04ca 100644 --- a/mobile/lib/domain/models/log.model.dart +++ b/mobile/lib/domain/models/log.model.dart @@ -1,16 +1,5 @@ /// Log levels according to dart logging [Level] -enum LogLevel { - all, - finest, - finer, - fine, - config, - info, - warning, - severe, - shout, - off, -} +enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off } class LogMessage { final String message; diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index 39a6d4518b..40117c5ac6 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -13,28 +13,18 @@ enum MemoryTypeEnum { class MemoryData { final int year; - const MemoryData({ - required this.year, - }); + const MemoryData({required this.year}); - MemoryData copyWith({ - int? year, - }) { - return MemoryData( - year: year ?? this.year, - ); + MemoryData copyWith({int? year}) { + return MemoryData(year: year ?? this.year); } Map toMap() { - return { - 'year': year, - }; + return {'year': year}; } factory MemoryData.fromMap(Map map) { - return MemoryData( - year: map['year'] as int, - ); + return MemoryData(year: map['year'] as int); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/domain/models/search_result.model.dart b/mobile/lib/domain/models/search_result.model.dart index e8c9429432..bae8b8e821 100644 --- a/mobile/lib/domain/models/search_result.model.dart +++ b/mobile/lib/domain/models/search_result.model.dart @@ -5,21 +5,12 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); int get totalAssets => assets.length; - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index 150545eba9..f427d93285 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -8,8 +8,7 @@ enum Setting { loadOriginalVideo(StoreKey.loadOriginalVideo, false), preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), - enableBackup(StoreKey.enableBackup, false), - ; + enableBackup(StoreKey.enableBackup, false); const Setting(this.storeKey, this.defaultValue); diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index 0db65d105b..d5ccf5558d 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -14,13 +14,7 @@ class Stack { required this.primaryAssetId, }); - Stack copyWith({ - String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId, - }) { + Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) { return Stack( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -63,11 +57,7 @@ class StackResponse { final String primaryAssetId; final List assetIds; - const StackResponse({ - required this.id, - required this.primaryAssetId, - required this.assetIds, - }); + const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds}); @override bool operator ==(covariant StackResponse other) { diff --git a/mobile/lib/domain/models/timeline.model.dart b/mobile/lib/domain/models/timeline.model.dart index 3751500f0f..d4cc5ab5c6 100644 --- a/mobile/lib/domain/models/timeline.model.dart +++ b/mobile/lib/domain/models/timeline.model.dart @@ -1,18 +1,8 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; -enum GroupAssetsBy { - day, - month, - auto, - none; -} +enum GroupAssetsBy { day, month, auto, none } -enum HeaderType { - none, - month, - day, - monthAndDay; -} +enum HeaderType { none, month, day, monthAndDay } class Bucket { final int assetCount; diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 9af8abadc2..1e83fa498d 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -74,22 +74,21 @@ quotaSizeInBytes: $quotaSizeInBytes, bool? isPartnerSharedWith, int? quotaUsageInBytes, int? quotaSizeInBytes, - }) => - UserDto( - id: id ?? this.id, - email: email ?? this.email, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, - avatarColor: avatarColor ?? this.avatarColor, - memoryEnabled: memoryEnabled ?? this.memoryEnabled, - inTimeline: inTimeline ?? this.inTimeline, - isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - ); + }) => UserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + updatedAt: updatedAt ?? this.updatedAt, + profileImagePath: profileImagePath ?? this.profileImagePath, + avatarColor: avatarColor ?? this.avatarColor, + memoryEnabled: memoryEnabled ?? this.memoryEnabled, + inTimeline: inTimeline ?? this.inTimeline, + isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + ); @override bool operator ==(covariant UserDto other) { @@ -143,13 +142,7 @@ class PartnerUserDto { this.profileImagePath, }); - PartnerUserDto copyWith({ - String? id, - String? email, - String? name, - bool? inTimeline, - String? profileImagePath, - }) { + PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) { return PartnerUserDto( id: id ?? this.id, email: email ?? this.email, diff --git a/mobile/lib/domain/models/user_metadata.model.dart b/mobile/lib/domain/models/user_metadata.model.dart index 8b7ca1ffa9..1c371a9d3e 100644 --- a/mobile/lib/domain/models/user_metadata.model.dart +++ b/mobile/lib/domain/models/user_metadata.model.dart @@ -24,17 +24,17 @@ enum AvatarColor { const AvatarColor(this.value); Color toColor({bool isDarkTheme = false}) => switch (this) { - AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), - }; + AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; } class Onboarding { @@ -193,17 +193,9 @@ class License { final String activationKey; final String licenseKey; - const License({ - required this.activatedAt, - required this.activationKey, - required this.licenseKey, - }); + const License({required this.activatedAt, required this.activationKey, required this.licenseKey}); - License copyWith({ - DateTime? activatedAt, - String? activationKey, - String? licenseKey, - }) { + License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) { return License( activatedAt: activatedAt ?? this.activatedAt, activationKey: activationKey ?? this.activationKey, @@ -255,16 +247,11 @@ class UserMetadata { final Preferences? preferences; final License? license; - const UserMetadata({ - required this.userId, - required this.key, - this.onboarding, - this.preferences, - this.license, - }) : assert( - onboarding != null || preferences != null || license != null, - 'One of onboarding, preferences and license must be provided', - ); + const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license}) + : assert( + onboarding != null || preferences != null || license != null, + 'One of onboarding, preferences and license must be provided', + ); UserMetadata copyWith({ String? userId, diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 5006e2d45c..c8cc61314e 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -13,9 +13,9 @@ class AssetService { const AssetService({ required RemoteAssetRepository remoteAssetRepository, required DriftLocalAssetRepository localAssetRepository, - }) : _remoteAssetRepository = remoteAssetRepository, - _localAssetRepository = localAssetRepository, - _platform = const LocalPlatform(); + }) : _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository, + _platform = const LocalPlatform(); Stream watchAsset(BaseAsset asset) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 3bbb75b003..2eb9aec4db 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -25,19 +25,16 @@ class HashService { required NativeSyncApi nativeSyncApi, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _localAlbumRepository = localAlbumRepository, - _localAssetRepository = localAssetRepository, - _storageRepository = storageRepository, - _nativeSyncApi = nativeSyncApi; + }) : _localAlbumRepository = localAlbumRepository, + _localAssetRepository = localAssetRepository, + _storageRepository = storageRepository, + _nativeSyncApi = nativeSyncApi; Future hashAssets() async { final Stopwatch stopwatch = Stopwatch()..start(); // Sorted by backupSelection followed by isCloud final localAlbums = await _localAlbumRepository.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); for (final album in localAlbums) { diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 4204761054..cb1bb40619 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -21,9 +21,9 @@ class LocalSyncService { required DriftLocalAlbumRepository localAlbumRepository, required NativeSyncApi nativeSyncApi, Platform? platform, - }) : _localAlbumRepository = localAlbumRepository, - _nativeSyncApi = nativeSyncApi, - _platform = platform ?? const LocalPlatform(); + }) : _localAlbumRepository = localAlbumRepository, + _nativeSyncApi = nativeSyncApi, + _platform = platform ?? const LocalPlatform(); Future sync({bool full = false}) async { final Stopwatch stopwatch = Stopwatch()..start(); @@ -70,9 +70,7 @@ class LocalSyncService { for (final album in cloudAlbums) { 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.", - ); + _log.warning("Cloud album ${album.name} not found in local database. Skipping sync."); continue; } await updateAlbum(dbAlbum, album); @@ -120,10 +118,7 @@ class LocalSyncService { final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : []; - await _localAlbumRepository.upsert( - album, - toUpsert: assets.toLocalAssets(), - ); + await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets()); _log.fine("Successfully added device album ${album.name}"); } catch (e, s) { _log.warning("Error while adding device album", e, s); @@ -146,9 +141,7 @@ class LocalSyncService { _log.fine("Syncing device album ${dbAlbum.name}"); if (_albumsEqual(deviceAlbum, dbAlbum)) { - _log.fine( - "Device album ${dbAlbum.name} has not changed. Skipping sync.", - ); + _log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync."); return false; } @@ -172,10 +165,7 @@ class LocalSyncService { @visibleForTesting // The [deviceAlbum] is expected to be refreshed before calling this method // with modified time and asset count - Future checkAddition( - LocalAlbum dbAlbum, - LocalAlbum deviceAlbum, - ) async { + Future checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { _log.fine("Fast syncing device album ${dbAlbum.name}"); // Assets has been modified @@ -189,9 +179,7 @@ class LocalSyncService { // Early return if no new assets were found if (newAssetsCount == 0) { - _log.fine( - "No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}", - ); + _log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}"); return false; } @@ -201,10 +189,7 @@ class LocalSyncService { return false; } - final newAssets = await _nativeSyncApi.getAssetsForAlbum( - deviceAlbum.id, - updatedTimeCond: updatedTime, - ); + final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), @@ -229,9 +214,7 @@ class LocalSyncService { final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : []; if (deviceAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Removing assets from DB.", - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB."); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), toDelete: assetsInDb.map((a) => a.id), @@ -239,18 +222,11 @@ class LocalSyncService { return true; } - final updatedDeviceAlbum = deviceAlbum.copyWith( - backupSelection: dbAlbum.backupSelection, - ); + final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection); if (dbAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Adding assets to DB.", - ); - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsInDevice, - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB."); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice); return true; } @@ -282,18 +258,12 @@ class LocalSyncService { ); if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { - _log.fine( - "No asset changes detected in album ${deviceAlbum.name}. Updating metadata.", - ); + _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); _localAlbumRepository.upsert(updatedDeviceAlbum); return true; } - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsToUpsert, - toDelete: assetsToDelete, - ); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete); return true; } catch (e, s) { diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index 72fb4d9bf7..ff72ec5502 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -61,11 +61,7 @@ class LogService { return instance; } - LogService._( - this._logRepository, - this._storeRepository, - this._shouldBuffer, - ) { + LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) { _logSubscription = Logger.root.onRecord.listen(_handleLogRecord); } @@ -89,10 +85,7 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer( - const Duration(seconds: 5), - () => unawaited(flushBuffer()), - ); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer())); } else { unawaited(_logRepository.insert(record)); } diff --git a/mobile/lib/domain/services/partner.service.dart b/mobile/lib/domain/services/partner.service.dart index 11299b9d6d..7733b5be6b 100644 --- a/mobile/lib/domain/services/partner.service.dart +++ b/mobile/lib/domain/services/partner.service.dart @@ -7,10 +7,7 @@ class DriftPartnerService { final DriftPartnerRepository _driftPartnerRepository; final PartnerApiRepository _partnerApiRepository; - const DriftPartnerService( - this._driftPartnerRepository, - this._partnerApiRepository, - ); + const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository); Future> getSharedWith(String userId) { return _driftPartnerRepository.getSharedWith(userId); @@ -20,9 +17,7 @@ class DriftPartnerService { return _driftPartnerRepository.getSharedBy(userId); } - Future> getAvailablePartners( - String currentUserId, - ) async { + Future> getAvailablePartners(String currentUserId) async { final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId); final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId); final available = otherUsers.where((user) { @@ -39,10 +34,7 @@ class DriftPartnerService { return; } - await _partnerApiRepository.update( - partnerId, - inTimeline: !partner.inTimeline, - ); + await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline); await _driftPartnerRepository.toggleShowInTimeline(partner, userId); } diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index 6c3b2e32af..f6c596f24a 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -26,11 +26,7 @@ class RemoteAlbumService { return _repository.get(albumId); } - List sortAlbums( - List albums, - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { + List sortAlbums(List albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) { return sortMode.sortFn(albums, isReverse); } @@ -69,16 +65,8 @@ class RemoteAlbumService { return filtered; } - Future createAlbum({ - required String title, - required List assetIds, - String? description, - }) async { - final album = await _albumApiRepository.createDriftAlbum( - title, - description: description, - assetIds: assetIds, - ); + Future createAlbum({required String title, required List assetIds, String? description}) async { + final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds); await _repository.create(album, assetIds); @@ -120,14 +108,8 @@ class RemoteAlbumService { return _repository.getAssets(albumId); } - Future addAssets({ - required String albumId, - required List assetIds, - }) async { - final album = await _albumApiRepository.addAssets( - albumId, - assetIds, - ); + Future addAssets({required String albumId, required List assetIds}) async { + final album = await _albumApiRepository.addAssets(albumId, assetIds); await _repository.addAssets(albumId, album.added); @@ -140,10 +122,7 @@ class RemoteAlbumService { await _repository.deleteAlbum(albumId); } - Future addUsers({ - required String albumId, - required List userIds, - }) async { + Future addUsers({required String albumId, required List userIds}) async { await _albumApiRepository.addUsers(albumId, userIds); return _repository.addUsers(albumId, userIds); diff --git a/mobile/lib/domain/services/search.service.dart b/mobile/lib/domain/services/search.service.dart index 052a2ca9da..6ccc5a97bf 100644 --- a/mobile/lib/domain/services/search.service.dart +++ b/mobile/lib/domain/services/search.service.dart @@ -83,10 +83,10 @@ extension on AssetResponseDto { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index bd839a18ec..dc845b70f1 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -24,16 +24,12 @@ class StoreService { } // TODO: Replace the implementation with the one from create after removing the typedef - static Future init({ - required IsarStoreRepository storeRepository, - }) async { + static Future init({required IsarStoreRepository storeRepository}) async { _instance ??= await create(storeRepository: storeRepository); return _instance!; } - static Future create({ - required IsarStoreRepository storeRepository, - }) async { + static Future create({required IsarStoreRepository storeRepository}) async { final instance = StoreService._(storeRepository: storeRepository); await instance._populateCache(); instance._storeUpdateSubscription = instance._listenForChange(); @@ -48,8 +44,8 @@ class StoreService { } StreamSubscription _listenForChange() => _storeRepository.watchAll().listen((event) { - _cache[event.key.id] = event.value; - }); + _cache[event.key.id] = event.value; + }); /// Disposes the store and cancels the subscription. To reuse the store call init() again void dispose() async { diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index a985008251..c21a9cade5 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -18,9 +18,9 @@ class SyncStreamService { required SyncApiRepository syncApiRepository, required SyncStreamRepository syncStreamRepository, bool Function()? cancelChecker, - }) : _syncApiRepository = syncApiRepository, - _syncStreamRepository = syncStreamRepository, - _cancelChecker = cancelChecker; + }) : _syncApiRepository = syncApiRepository, + _syncStreamRepository = syncStreamRepository, + _cancelChecker = cancelChecker; bool get isCancelled => _cancelChecker?.call() ?? false; @@ -34,9 +34,7 @@ class SyncStreamService { Future handleWsAssetUploadReadyV1Batch(List batchData) async { if (batchData.isEmpty) return; - _logger.info( - 'Processing batch of ${batchData.length} AssetUploadReadyV1 events', - ); + _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events'); final List assets = []; final List exifs = []; @@ -65,22 +63,12 @@ class SyncStreamService { } if (assets.isNotEmpty && exifs.isNotEmpty) { - await _syncStreamRepository.updateAssetsV1( - assets, - debugLabel: 'websocket-batch', - ); - await _syncStreamRepository.updateAssetsExifV1( - exifs, - debugLabel: 'websocket-batch', - ); + await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch'); + await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch'); _logger.info('Successfully processed ${assets.length} assets in batch'); } } catch (error, stackTrace) { - _logger.severe( - "Error processing AssetUploadReadyV1 websocket batch events", - error, - stackTrace, - ); + _logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace); } } @@ -114,10 +102,7 @@ class SyncStreamService { batch.clear(); } - Future _handleSyncData( - SyncEntityType type, - Iterable data, - ) async { + Future _handleSyncData(SyncEntityType type, Iterable data) async { _logger.fine("Processing sync data for $type of length ${data.length}"); switch (type) { case SyncEntityType.userV1: @@ -135,30 +120,15 @@ class SyncStreamService { case SyncEntityType.assetExifV1: return _syncStreamRepository.updateAssetsExifV1(data.cast()); case SyncEntityType.partnerAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerAssetDeleteV1: - return _syncStreamRepository.deleteAssetsV1( - data.cast(), - debugLabel: "partner", - ); + return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner"); case SyncEntityType.partnerAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.albumV1: return _syncStreamRepository.updateAlbumsV1(data.cast()); case SyncEntityType.albumDeleteV1: @@ -166,39 +136,21 @@ class SyncStreamService { case SyncEntityType.albumUserV1: return _syncStreamRepository.updateAlbumUsersV1(data.cast()); case SyncEntityType.albumUserBackfillV1: - return _syncStreamRepository.updateAlbumUsersV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumUserDeleteV1: return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); case SyncEntityType.albumAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumToAssetV1: return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); case SyncEntityType.albumToAssetBackfillV1: - return _syncStreamRepository.updateAlbumToAssetsV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumToAssetDeleteV1: return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); // No-op. SyncAckV1 entities are checkpoints in the sync stream @@ -218,28 +170,15 @@ class SyncStreamService { case SyncEntityType.stackDeleteV1: return _syncStreamRepository.deleteStacksV1(data.cast()); case SyncEntityType.partnerStackV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerStackBackfillV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerStackDeleteV1: - return _syncStreamRepository.deleteStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.userMetadataV1: - return _syncStreamRepository.updateUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.updateUserMetadatasV1(data.cast()); case SyncEntityType.userMetadataDeleteV1: - return _syncStreamRepository.deleteUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.deleteUserMetadatasV1(data.cast()); case SyncEntityType.personV1: return _syncStreamRepository.updatePeopleV1(data.cast()); case SyncEntityType.personDeleteV1: diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 7e982558b7..7c22fb786d 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -11,27 +11,19 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; -typedef TimelineAssetSource = Future> Function( - int index, - int count, -); +typedef TimelineAssetSource = Future> Function(int index, int count); typedef TimelineBucketSource = Stream> Function(); -typedef TimelineQuery = ({ - TimelineAssetSource assetSource, - TimelineBucketSource bucketSource, -}); +typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource}); class TimelineFactory { final DriftTimelineRepository _timelineRepository; final SettingsService _settingsService; - const TimelineFactory({ - required DriftTimelineRepository timelineRepository, - required SettingsService settingsService, - }) : _timelineRepository = timelineRepository, - _settingsService = settingsService; + const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService}) + : _timelineRepository = timelineRepository, + _settingsService = settingsService; GroupAssetsBy get groupBy { final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; @@ -75,17 +67,11 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService(TimelineQuery query) - : this._( - assetSource: query.assetSource, - bucketSource: query.bucketSource, - ); + TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource); - TimelineService._({ - required TimelineAssetSource assetSource, - required TimelineBucketSource bucketSource, - }) : _assetSource = assetSource, - _bucketSource = bucketSource { + TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource}) + : _assetSource = assetSource, + _bucketSource = bucketSource { _bucketSubscription = _bucketSource().listen((buckets) { _mutex.run(() async { final totalAssets = buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); @@ -103,10 +89,7 @@ class TimelineService { count = kTimelineAssetLoadBatchSize; } else { offset = _bufferOffset; - count = math.min( - _buffer.length, - totalAssets - _bufferOffset, - ); + count = math.min(_buffer.length, totalAssets - _bufferOffset); } _buffer = await _assetSource(offset, count); _bufferOffset = offset; @@ -134,10 +117,7 @@ class TimelineService { // make sure to load a meaningful amount of data (and not only the requested slice) // otherwise, each call to [loadAssets] would result in DB call trashing performance // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests - final len = math.max( - kTimelineAssetLoadBatchSize, - count + kTimelineAssetLoadOppositeSize, - ); + final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize); // when scrolling forward, start shortly before the requested offset // when scrolling backward, end shortly after the requested offset to guard against the user scrolling // in the other direction a tiny bit resulting in another required load from the DB diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 14fed9fb93..3e948fe0f5 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -18,9 +18,9 @@ class UserService { required IsarUserRepository isarUserRepository, required UserApiRepository userApiRepository, required StoreService storeService, - }) : _isarUserRepository = isarUserRepository, - _userApiRepository = userApiRepository, - _storeService = storeService; + }) : _isarUserRepository = isarUserRepository, + _userApiRepository = userApiRepository, + _storeService = storeService; UserDto getMyUser() { return _storeService.get(StoreKey.currentUser); @@ -44,10 +44,7 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { - final path = await _userApiRepository.createProfileImage( - name: name, - data: image, - ); + final path = await _userApiRepository.createProfileImage(name: name, data: image); final updatedUser = getMyUser().copyWith(profileImagePath: path); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 1524c412f1..1944591c93 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -66,23 +66,21 @@ class BackgroundSyncManager { // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full - ? runInIsolateGentle( - computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true), - ) - : runInIsolateGentle( - computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false), - ); + ? runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true)) + : runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false)); - return _deviceAlbumSyncTask!.whenComplete(() { - _deviceAlbumSyncTask = null; - onLocalSyncComplete?.call(); - }).catchError((error) { - onLocalSyncError?.call(error.toString()); - _deviceAlbumSyncTask = null; - }); + return _deviceAlbumSyncTask! + .whenComplete(() { + _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }) + .catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; + }); } -// No need to cancel the task, as it can also be run when the user logs out + // No need to cancel the task, as it can also be run when the user logs out Future hashAssets() { if (_hashTask != null) { return _hashTask!.future; @@ -90,17 +88,17 @@ class BackgroundSyncManager { onHashingStart?.call(); - _hashTask = runInIsolateGentle( - computation: (ref) => ref.read(hashServiceProvider).hashAssets(), - ); + _hashTask = runInIsolateGentle(computation: (ref) => ref.read(hashServiceProvider).hashAssets()); - return _hashTask!.whenComplete(() { - onHashingComplete?.call(); - _hashTask = null; - }).catchError((error) { - onHashingError?.call(error.toString()); - _hashTask = null; - }); + return _hashTask! + .whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }) + .catchError((error) { + onHashingError?.call(error.toString()); + _hashTask = null; + }); } Future syncRemote() { @@ -110,16 +108,16 @@ class BackgroundSyncManager { onRemoteSyncStart?.call(); - _syncTask = runInIsolateGentle( - computation: (ref) => ref.read(syncStreamServiceProvider).sync(), - ); - return _syncTask!.whenComplete(() { - onRemoteSyncComplete?.call(); - _syncTask = null; - }).catchError((error) { - onRemoteSyncError?.call(error.toString()); - _syncTask = null; - }); + _syncTask = runInIsolateGentle(computation: (ref) => ref.read(syncStreamServiceProvider).sync()); + return _syncTask! + .whenComplete(() { + onRemoteSyncComplete?.call(); + _syncTask = null; + }) + .catchError((error) { + onRemoteSyncError?.call(error.toString()); + _syncTask = null; + }); } Future syncWebsocketBatch(List batchData) { @@ -133,9 +131,6 @@ class BackgroundSyncManager { } } -Cancelable _handleWsAssetUploadReadyV1Batch( - List batchData, -) => - runInIsolateGentle( - computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), - ); +Cancelable _handleWsAssetUploadReadyV1Batch(List batchData) => runInIsolateGentle( + computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), +); diff --git a/mobile/lib/domain/utils/event_stream.dart b/mobile/lib/domain/utils/event_stream.dart index 008ddef183..5967fdca50 100644 --- a/mobile/lib/domain/utils/event_stream.dart +++ b/mobile/lib/domain/utils/event_stream.dart @@ -28,12 +28,7 @@ class EventStream { void Function()? onDone, bool? cancelOnError, }) { - return where().listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); + return where().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } /// Closes the stream controller diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index a7cb612d6e..2ca0d50dcc 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -113,10 +113,7 @@ class Album { modifiedAt.isAtSameMomentAs(other.modifiedAt) && isAtSameMomentAs(startDate, other.startDate) && isAtSameMomentAs(endDate, other.endDate) && - isAtSameMomentAs( - lastModifiedAssetTimestamp, - other.lastModifiedAssetTimestamp, - ) && + isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) && shared == other.shared && activityEnabled == other.activityEnabled && owner.value == other.owner.value && @@ -169,9 +166,7 @@ class Album { a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst(); } if (dto.albumUsers.isNotEmpty) { - final users = await db.users.getAllById( - dto.albumUsers.map((e) => e.user.id).toList(growable: false), - ); + final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false)); a.sharedUsers.addAll(users.cast()); } if (dto.assets.isNotEmpty) { diff --git a/mobile/lib/entities/album.entity.g.dart b/mobile/lib/entities/album.entity.g.dart index 546101baca..e6ecde7f9a 100644 --- a/mobile/lib/entities/album.entity.g.dart +++ b/mobile/lib/entities/album.entity.g.dart @@ -42,31 +42,19 @@ const AlbumSchema = CollectionSchema( name: r'lastModifiedAssetTimestamp', type: IsarType.dateTime, ), - r'localId': PropertySchema( - id: 5, - name: r'localId', - type: IsarType.string, - ), + r'localId': PropertySchema(id: 5, name: r'localId', type: IsarType.string), r'modifiedAt': PropertySchema( id: 6, name: r'modifiedAt', type: IsarType.dateTime, ), - r'name': PropertySchema( - id: 7, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 7, name: r'name', type: IsarType.string), r'remoteId': PropertySchema( id: 8, name: r'remoteId', type: IsarType.string, ), - r'shared': PropertySchema( - id: 9, - name: r'shared', - type: IsarType.bool, - ), + r'shared': PropertySchema(id: 9, name: r'shared', type: IsarType.bool), r'sortOrder': PropertySchema( id: 10, name: r'sortOrder', @@ -77,8 +65,9 @@ const AlbumSchema = CollectionSchema( id: 11, name: r'startDate', type: IsarType.dateTime, - ) + ), }, + estimateSize: _albumEstimateSize, serialize: _albumSerialize, deserialize: _albumDeserialize, @@ -95,7 +84,7 @@ const AlbumSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -108,9 +97,9 @@ const AlbumSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: { r'owner': LinkSchema( @@ -136,9 +125,10 @@ const AlbumSchema = CollectionSchema( name: r'assets', target: r'Asset', single: false, - ) + ), }, embeddedSchemas: {}, + getId: _albumGetId, getLinks: _albumGetLinks, attach: _albumAttach, @@ -212,7 +202,7 @@ Album _albumDeserialize( shared: reader.readBool(offsets[9]), sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ?? - SortOrder.desc, + SortOrder.desc, startDate: reader.readDateTimeOrNull(offsets[11]), ); object.id = id; @@ -248,7 +238,8 @@ P _albumDeserializeProp

( return (reader.readBool(offset)) as P; case 10: return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ?? - SortOrder.desc) as P; + SortOrder.desc) + as P; case 11: return (reader.readDateTimeOrNull(offset)) as P; default: @@ -256,14 +247,8 @@ P _albumDeserializeProp

( } } -const _AlbumsortOrderEnumValueMap = { - 'asc': 0, - 'desc': 1, -}; -const _AlbumsortOrderValueEnumMap = { - 0: SortOrder.asc, - 1: SortOrder.desc, -}; +const _AlbumsortOrderEnumValueMap = {'asc': 0, 'desc': 1}; +const _AlbumsortOrderValueEnumMap = {0: SortOrder.asc, 1: SortOrder.desc}; Id _albumGetId(Album object) { return object.id; @@ -277,8 +262,12 @@ void _albumAttach(IsarCollection col, Id id, Album object) { object.id = id; object.owner.attach(col, col.isar.collection(), r'owner', id); object.thumbnail.attach(col, col.isar.collection(), r'thumbnail', id); - object.sharedUsers - .attach(col, col.isar.collection(), r'sharedUsers', id); + object.sharedUsers.attach( + col, + col.isar.collection(), + r'sharedUsers', + id, + ); object.assets.attach(col, col.isar.collection(), r'assets', id); } @@ -293,10 +282,7 @@ extension AlbumQueryWhereSort on QueryBuilder { extension AlbumQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -322,8 +308,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -331,8 +319,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -347,141 +337,163 @@ extension AlbumQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } @@ -489,22 +501,22 @@ extension AlbumQueryWhere on QueryBuilder { extension AlbumQueryFilter on QueryBuilder { QueryBuilder activityEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'activityEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'activityEnabled', value: value), + ); }); } QueryBuilder createdAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } @@ -513,11 +525,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -526,11 +540,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -541,29 +557,31 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -572,11 +590,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -586,12 +606,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -601,12 +623,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -618,14 +642,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,11 +660,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -647,79 +675,85 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder endDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'endDate'), + ); }); } QueryBuilder endDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'endDate'), + ); }); } QueryBuilder endDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'endDate', value: value), + ); }); } @@ -728,11 +762,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -741,11 +777,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -756,22 +794,23 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'endDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'endDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -780,11 +819,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -793,11 +834,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -808,103 +851,112 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNull() { + lastModifiedAssetTimestampIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lastModifiedAssetTimestamp'), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNotNull() { + lastModifiedAssetTimestampIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'lastModifiedAssetTimestamp', + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampEqualTo(DateTime? value) { + lastModifiedAssetTimestampEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampGreaterThan( + lastModifiedAssetTimestampGreaterThan( DateTime? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampLessThan( - DateTime? value, { - bool include = false, - }) { + lastModifiedAssetTimestampLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampBetween( + lastModifiedAssetTimestampBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastModifiedAssetTimestamp', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastModifiedAssetTimestamp', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -913,11 +965,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -927,12 +981,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -942,12 +998,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -959,14 +1017,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -975,11 +1035,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -988,63 +1050,69 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder modifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedAt', value: value), + ); }); } @@ -1053,11 +1121,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1066,11 +1136,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1081,13 +1153,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1096,11 +1170,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1110,12 +1186,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1125,12 +1203,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1142,14 +1222,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1158,11 +1240,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1171,67 +1255,75 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1240,11 +1332,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1254,12 +1348,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1269,12 +1365,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1286,14 +1384,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1302,11 +1402,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1315,72 +1417,77 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder sharedEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'shared', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'shared', value: value), + ); }); } QueryBuilder sortOrderEqualTo( - SortOrder value) { + SortOrder value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'sortOrder', value: value), + ); }); } @@ -1389,11 +1496,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1402,11 +1511,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1417,39 +1528,41 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'sortOrder', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'sortOrder', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder startDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'startDate'), + ); }); } QueryBuilder startDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'startDate'), + ); }); } QueryBuilder startDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'startDate', value: value), + ); }); } @@ -1458,11 +1571,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1471,11 +1586,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1486,13 +1603,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'startDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'startDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1513,7 +1632,8 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder thumbnail( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'thumbnail'); }); @@ -1526,14 +1646,16 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder sharedUsers( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'sharedUsers'); }); } QueryBuilder sharedUsersLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, true, length, true); }); @@ -1561,10 +1683,7 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder - sharedUsersLengthGreaterThan( - int length, { - bool include = false, - }) { + sharedUsersLengthGreaterThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, include, 999999, true); }); @@ -1578,19 +1697,26 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'sharedUsers', lower, includeLower, upper, includeUpper); + r'sharedUsers', + lower, + includeLower, + upper, + includeUpper, + ); }); } QueryBuilder assets( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'assets'); }); } QueryBuilder assetsLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'assets', length, true, length, true); }); @@ -1634,7 +1760,12 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'assets', lower, includeLower, upper, includeUpper); + r'assets', + lower, + includeLower, + upper, + includeUpper, + ); }); } } @@ -1695,7 +1826,7 @@ extension AlbumQuerySortBy on QueryBuilder { } QueryBuilder - sortByLastModifiedAssetTimestampDesc() { + sortByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1854,7 +1985,7 @@ extension AlbumQuerySortThenBy on QueryBuilder { } QueryBuilder - thenByLastModifiedAssetTimestampDesc() { + thenByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1958,8 +2089,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -1977,8 +2109,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -1990,15 +2123,17 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -2055,7 +2190,7 @@ extension AlbumQueryProperty on QueryBuilder { } QueryBuilder - lastModifiedAssetTimestampProperty() { + lastModifiedAssetTimestampProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'lastModifiedAssetTimestamp'); }); diff --git a/mobile/lib/entities/android_device_asset.entity.g.dart b/mobile/lib/entities/android_device_asset.entity.g.dart index eaa7658565..9034709b8e 100644 --- a/mobile/lib/entities/android_device_asset.entity.g.dart +++ b/mobile/lib/entities/android_device_asset.entity.g.dart @@ -18,12 +18,9 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'AndroidDeviceAsset', id: -6758387181232899335, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), }, + estimateSize: _androidDeviceAssetEstimateSize, serialize: _androidDeviceAssetSerialize, deserialize: _androidDeviceAssetDeserialize, @@ -40,12 +37,13 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _androidDeviceAssetGetId, getLinks: _androidDeviceAssetGetLinks, attach: _androidDeviceAssetAttach, @@ -103,12 +101,16 @@ Id _androidDeviceAssetGetId(AndroidDeviceAsset object) { } List> _androidDeviceAssetGetLinks( - AndroidDeviceAsset object) { + AndroidDeviceAsset object, +) { return []; } void _androidDeviceAssetAttach( - IsarCollection col, Id id, AndroidDeviceAsset object) { + IsarCollection col, + Id id, + AndroidDeviceAsset object, +) { object.id = id; } @@ -124,17 +126,14 @@ extension AndroidDeviceAssetQueryWhereSort extension AndroidDeviceAssetQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -157,7 +156,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -166,7 +165,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -175,63 +174,72 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -240,134 +248,97 @@ extension AndroidDeviceAssetQueryWhere extension AndroidDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -385,58 +356,57 @@ extension AndroidDeviceAssetQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -453,14 +423,14 @@ extension AndroidDeviceAssetQuerySortBy extension AndroidDeviceAssetQuerySortThenBy on QueryBuilder { QueryBuilder - thenById() { + thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); }); } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); @@ -470,7 +440,7 @@ extension AndroidDeviceAssetQuerySortThenBy extension AndroidDeviceAssetQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index f5c577a060..0d549457a1 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -19,30 +19,30 @@ part 'asset.entity.g.dart'; @Collection(inheritance: false) class Asset { Asset.remote(AssetResponseDto remote) - : remoteId = remote.id, - checksum = remote.checksum, - fileCreatedAt = remote.fileCreatedAt, - fileModifiedAt = remote.fileModifiedAt, - updatedAt = remote.updatedAt, - durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, - type = remote.type.toAssetType(), - fileName = remote.originalFileName, - height = remote.exifInfo?.exifImageHeight?.toInt(), - width = remote.exifInfo?.exifImageWidth?.toInt(), - livePhotoVideoId = remote.livePhotoVideoId, - ownerId = fastHash(remote.ownerId), - exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), - isFavorite = remote.isFavorite, - isArchived = remote.isArchived, - isTrashed = remote.isTrashed, - isOffline = remote.isOffline, - // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app - // stack handling to properly handle it - stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, - stackCount = remote.stack?.assetCount ?? 0, - stackId = remote.stack?.id, - thumbhash = remote.thumbhash, - visibility = getVisibility(remote.visibility); + : remoteId = remote.id, + checksum = remote.checksum, + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, + durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, + type = remote.type.toAssetType(), + fileName = remote.originalFileName, + height = remote.exifInfo?.exifImageHeight?.toInt(), + width = remote.exifInfo?.exifImageWidth?.toInt(), + livePhotoVideoId = remote.livePhotoVideoId, + ownerId = fastHash(remote.ownerId), + exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), + isFavorite = remote.isFavorite, + isArchived = remote.isArchived, + isTrashed = remote.isTrashed, + isOffline = remote.isOffline, + // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app + // stack handling to properly handle it + stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, + stackCount = remote.stack?.assetCount ?? 0, + stackId = remote.stack?.id, + thumbhash = remote.thumbhash, + visibility = getVisibility(remote.visibility); Asset({ this.id = Isar.autoIncrement, @@ -127,11 +127,7 @@ class Asset { @Index(unique: false, replace: false, type: IndexType.hash) String? localId; - @Index( - unique: true, - replace: false, - composite: [CompositeIndex("checksum", type: IndexType.hash)], - ) + @Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)]) int ownerId; DateTime fileCreatedAt; @@ -447,33 +443,32 @@ class Asset { int? stackCount, String? thumbhash, AssetVisibilityEnum? visibility, - }) => - Asset( - id: id ?? this.id, - checksum: checksum ?? this.checksum, - remoteId: remoteId ?? this.remoteId, - localId: localId ?? this.localId, - ownerId: ownerId ?? this.ownerId, - fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, - fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, - updatedAt: updatedAt ?? this.updatedAt, - durationInSeconds: durationInSeconds ?? this.durationInSeconds, - type: type ?? this.type, - width: width ?? this.width, - height: height ?? this.height, - fileName: fileName ?? this.fileName, - livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, - isFavorite: isFavorite ?? this.isFavorite, - isArchived: isArchived ?? this.isArchived, - isTrashed: isTrashed ?? this.isTrashed, - isOffline: isOffline ?? this.isOffline, - exifInfo: exifInfo ?? this.exifInfo, - stackId: stackId ?? this.stackId, - stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, - stackCount: stackCount ?? this.stackCount, - thumbhash: thumbhash ?? this.thumbhash, - visibility: visibility ?? this.visibility, - ); + }) => Asset( + id: id ?? this.id, + checksum: checksum ?? this.checksum, + remoteId: remoteId ?? this.remoteId, + localId: localId ?? this.localId, + ownerId: ownerId ?? this.ownerId, + fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, + fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, + updatedAt: updatedAt ?? this.updatedAt, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + type: type ?? this.type, + width: width ?? this.width, + height: height ?? this.height, + fileName: fileName ?? this.fileName, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + isFavorite: isFavorite ?? this.isFavorite, + isArchived: isArchived ?? this.isArchived, + isTrashed: isTrashed ?? this.isTrashed, + isOffline: isOffline ?? this.isOffline, + exifInfo: exifInfo ?? this.exifInfo, + stackId: stackId ?? this.stackId, + stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, + stackCount: stackCount ?? this.stackCount, + thumbhash: thumbhash ?? this.thumbhash, + visibility: visibility ?? this.visibility, + ); Future put(Isar db) async { await db.assets.put(this); @@ -494,10 +489,7 @@ class Asset { return compareByChecksum(a, b); } - static int compareByOwnerChecksumCreatedModified( - Asset a, - Asset b, - ) { + static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); if (ownerIdOrder != 0) return ownerIdOrder; final int checksumOrder = compareByChecksum(a, b); @@ -539,11 +531,11 @@ class Asset { } static getVisibility(AssetVisibility visibility) => switch (visibility) { - AssetVisibility.archive => AssetVisibilityEnum.archive, - AssetVisibility.hidden => AssetVisibilityEnum.hidden, - AssetVisibility.locked => AssetVisibilityEnum.locked, - AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, - }; + AssetVisibility.archive => AssetVisibilityEnum.archive, + AssetVisibility.hidden => AssetVisibilityEnum.hidden, + AssetVisibility.locked => AssetVisibilityEnum.locked, + AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, + }; } enum AssetType { @@ -556,21 +548,17 @@ enum AssetType { extension AssetTypeEnumHelper on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception(), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception(), + }; } /// Describes where the information of this asset came from: /// only from the local device, only from the remote server or merged from both -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } extension AssetsHelper on IsarCollection { Future deleteAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); @@ -579,13 +567,9 @@ extension AssetsHelper on IsarCollection { Future> getAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll(); Future getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst(); - QueryBuilder remote( - Iterable ids, - ) => + QueryBuilder remote(Iterable ids) => where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e)); - QueryBuilder local( - Iterable ids, - ) { + QueryBuilder local(Iterable ids) { return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e)); } } diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index b558690813..be5b427d01 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -42,11 +42,7 @@ const AssetSchema = CollectionSchema( name: r'fileName', type: IsarType.string, ), - r'height': PropertySchema( - id: 5, - name: r'height', - type: IsarType.int, - ), + r'height': PropertySchema(id: 5, name: r'height', type: IsarType.int), r'isArchived': PropertySchema( id: 6, name: r'isArchived', @@ -72,16 +68,8 @@ const AssetSchema = CollectionSchema( name: r'livePhotoVideoId', type: IsarType.string, ), - r'localId': PropertySchema( - id: 11, - name: r'localId', - type: IsarType.string, - ), - r'ownerId': PropertySchema( - id: 12, - name: r'ownerId', - type: IsarType.long, - ), + r'localId': PropertySchema(id: 11, name: r'localId', type: IsarType.string), + r'ownerId': PropertySchema(id: 12, name: r'ownerId', type: IsarType.long), r'remoteId': PropertySchema( id: 13, name: r'remoteId', @@ -92,11 +80,7 @@ const AssetSchema = CollectionSchema( name: r'stackCount', type: IsarType.long, ), - r'stackId': PropertySchema( - id: 15, - name: r'stackId', - type: IsarType.string, - ), + r'stackId': PropertySchema(id: 15, name: r'stackId', type: IsarType.string), r'stackPrimaryAssetId': PropertySchema( id: 16, name: r'stackPrimaryAssetId', @@ -124,12 +108,9 @@ const AssetSchema = CollectionSchema( type: IsarType.byte, enumMap: _AssetvisibilityEnumValueMap, ), - r'width': PropertySchema( - id: 21, - name: r'width', - type: IsarType.int, - ) + r'width': PropertySchema(id: 21, name: r'width', type: IsarType.int), }, + estimateSize: _assetEstimateSize, serialize: _assetSerialize, deserialize: _assetDeserialize, @@ -146,7 +127,7 @@ const AssetSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -159,7 +140,7 @@ const AssetSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'ownerId_checksum': IndexSchema( @@ -177,12 +158,13 @@ const AssetSchema = CollectionSchema( name: r'checksum', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _assetGetId, getLinks: _assetGetLinks, attach: _assetAttach, @@ -292,12 +274,13 @@ Asset _assetDeserialize( stackId: reader.readStringOrNull(offsets[15]), stackPrimaryAssetId: reader.readStringOrNull(offsets[16]), thumbhash: reader.readStringOrNull(offsets[17]), - type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? + type: + _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, updatedAt: reader.readDateTime(offsets[19]), visibility: _AssetvisibilityValueEnumMap[reader.readByteOrNull(offsets[20])] ?? - AssetVisibilityEnum.timeline, + AssetVisibilityEnum.timeline, width: reader.readIntOrNull(offsets[21]), ); return object; @@ -348,12 +331,14 @@ P _assetDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 18: return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetType.other) as P; + AssetType.other) + as P; case 19: return (reader.readDateTime(offset)) as P; case 20: return (_AssetvisibilityValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetVisibilityEnum.timeline) as P; + AssetVisibilityEnum.timeline) + as P; case 21: return (reader.readIntOrNull(offset)) as P; default: @@ -361,12 +346,7 @@ P _assetDeserializeProp

( } } -const _AssettypeEnumValueMap = { - 'other': 0, - 'image': 1, - 'video': 2, - 'audio': 3, -}; +const _AssettypeEnumValueMap = {'other': 0, 'image': 1, 'video': 2, 'audio': 3}; const _AssettypeValueEnumMap = { 0: AssetType.other, 1: AssetType.image, @@ -416,10 +396,14 @@ extension AssetByIndex on IsarCollection { } Future> getAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -429,10 +413,14 @@ extension AssetByIndex on IsarCollection { } List getAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -442,10 +430,14 @@ extension AssetByIndex on IsarCollection { } Future deleteAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -455,10 +447,14 @@ extension AssetByIndex on IsarCollection { } int deleteAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -479,10 +475,15 @@ extension AssetByIndex on IsarCollection { return putAllByIndex(r'ownerId_checksum', objects); } - List putAllByOwnerIdChecksumSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'ownerId_checksum', objects, - saveLinks: saveLinks); + List putAllByOwnerIdChecksumSync( + List objects, { + bool saveLinks = true, + }) { + return putAllByIndexSync( + r'ownerId_checksum', + objects, + saveLinks: saveLinks, + ); } } @@ -497,10 +498,7 @@ extension AssetQueryWhereSort on QueryBuilder { extension AssetQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -526,8 +524,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -535,8 +535,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -551,186 +553,220 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } QueryBuilder ownerIdEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId], + ), + ); }); } QueryBuilder ownerIdNotEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ); } }); } @@ -740,12 +776,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: include, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: include, + upper: [], + ), + ); }); } @@ -754,12 +792,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: include, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: include, + ), + ); }); } @@ -770,57 +810,71 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [lowerOwnerId], - includeLower: includeLower, - upper: [upperOwnerId], - includeUpper: includeUpper, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [lowerOwnerId], + includeLower: includeLower, + upper: [upperOwnerId], + includeUpper: includeUpper, + ), + ); }); } QueryBuilder ownerIdChecksumEqualTo( - int ownerId, String checksum) { + int ownerId, + String checksum, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId, checksum], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId, checksum], + ), + ); }); } QueryBuilder - ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { + ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ); } }); } @@ -832,11 +886,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -846,12 +902,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -861,12 +919,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -878,14 +938,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'checksum', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'checksum', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -894,11 +956,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -907,77 +971,82 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'checksum', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'checksum', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'checksum', value: ''), + ); }); } QueryBuilder checksumIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'checksum', value: ''), + ); }); } QueryBuilder durationInSecondsEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'durationInSeconds', value: value), + ); }); } QueryBuilder - durationInSecondsGreaterThan( - int value, { - bool include = false, - }) { + durationInSecondsGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -986,11 +1055,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -1001,23 +1072,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'durationInSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'durationInSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileCreatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileCreatedAt', value: value), + ); }); } @@ -1026,11 +1099,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1039,11 +1114,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1054,23 +1131,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileCreatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileCreatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileModifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileModifiedAt', value: value), + ); }); } @@ -1079,11 +1158,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1092,11 +1173,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1107,13 +1190,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileModifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileModifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1122,11 +1207,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1136,12 +1223,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1151,12 +1240,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1168,14 +1259,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileName', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1184,11 +1277,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1197,78 +1292,83 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'fileName', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'fileName', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileName', value: ''), + ); }); } QueryBuilder fileNameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'fileName', value: ''), + ); }); } QueryBuilder heightIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'height'), + ); }); } QueryBuilder heightIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'height'), + ); }); } QueryBuilder heightEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'height', value: value), + ); }); } @@ -1277,11 +1377,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1290,11 +1392,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1305,22 +1409,23 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'height', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'height', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1329,11 +1434,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1342,11 +1449,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1357,70 +1466,72 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isArchivedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isArchived', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isArchived', value: value), + ); }); } QueryBuilder isFavoriteEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isFavorite', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isFavorite', value: value), + ); }); } QueryBuilder isOfflineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isOffline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isOffline', value: value), + ); }); } QueryBuilder isTrashedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isTrashed', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isTrashed', value: value), + ); }); } QueryBuilder livePhotoVideoIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'livePhotoVideoId'), + ); }); } QueryBuilder - livePhotoVideoIdIsNotNull() { + livePhotoVideoIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'livePhotoVideoId'), + ); }); } @@ -1429,11 +1540,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1443,12 +1556,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1458,12 +1573,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1475,14 +1592,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'livePhotoVideoId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'livePhotoVideoId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1491,11 +1610,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1504,70 +1625,76 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'livePhotoVideoId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'livePhotoVideoId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder - livePhotoVideoIdIsNotEmpty() { + livePhotoVideoIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -1576,11 +1703,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1590,12 +1719,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1605,12 +1736,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1622,14 +1755,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1638,11 +1773,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1651,62 +1788,67 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder ownerIdEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'ownerId', value: value), + ); }); } @@ -1715,11 +1857,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1728,11 +1872,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1743,29 +1889,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'ownerId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'ownerId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1774,11 +1922,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1788,12 +1938,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1803,12 +1955,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1820,14 +1974,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1836,11 +1992,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1849,63 +2007,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder stackCountEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackCount', value: value), + ); }); } @@ -1914,11 +2078,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1927,11 +2093,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1942,29 +2110,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder stackIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackId'), + ); }); } QueryBuilder stackIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackId'), + ); }); } @@ -1973,11 +2143,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1987,12 +2159,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2002,12 +2176,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2019,14 +2195,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2035,11 +2213,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2048,71 +2228,77 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackId', value: ''), + ); }); } QueryBuilder stackIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'stackId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNull() { + stackPrimaryAssetIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackPrimaryAssetId'), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotNull() { + stackPrimaryAssetIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackPrimaryAssetId'), + ); }); } @@ -2121,27 +2307,31 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdGreaterThan( + stackPrimaryAssetIdGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2151,12 +2341,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2168,28 +2360,29 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackPrimaryAssetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackPrimaryAssetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + stackPrimaryAssetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2198,71 +2391,80 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackPrimaryAssetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackPrimaryAssetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdIsEmpty() { + stackPrimaryAssetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackPrimaryAssetId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotEmpty() { + stackPrimaryAssetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + property: r'stackPrimaryAssetId', + value: '', + ), + ); }); } QueryBuilder thumbhashIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'thumbhash'), + ); }); } QueryBuilder thumbhashIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'thumbhash'), + ); }); } @@ -2271,11 +2473,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2285,12 +2489,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,12 +2506,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2317,14 +2525,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'thumbhash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'thumbhash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2333,11 +2543,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2346,63 +2558,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'thumbhash', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'thumbhash', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'thumbhash', value: ''), + ); }); } QueryBuilder thumbhashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'thumbhash', value: ''), + ); }); } QueryBuilder typeEqualTo( - AssetType value) { + AssetType value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'type', value: value), + ); }); } @@ -2411,11 +2629,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2424,11 +2644,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2439,23 +2661,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'type', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -2464,11 +2688,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2477,11 +2703,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2492,23 +2720,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder visibilityEqualTo( - AssetVisibilityEnum value) { + AssetVisibilityEnum value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'visibility', value: value), + ); }); } @@ -2517,11 +2747,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2530,11 +2762,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2545,38 +2779,39 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'visibility', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'visibility', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder widthIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'width'), + ); }); } QueryBuilder widthIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'width'), + ); }); } QueryBuilder widthEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'width', value: value), + ); }); } @@ -2585,11 +2820,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2598,11 +2835,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2613,13 +2852,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'width', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'width', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -3173,8 +3414,9 @@ extension AssetQuerySortThenBy on QueryBuilder { } extension AssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByChecksum( - {bool caseSensitive = true}) { + QueryBuilder distinctByChecksum({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'checksum', caseSensitive: caseSensitive); }); @@ -3198,8 +3440,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByFileName( - {bool caseSensitive = true}) { + QueryBuilder distinctByFileName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'fileName', caseSensitive: caseSensitive); }); @@ -3235,16 +3478,20 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLivePhotoVideoId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLivePhotoVideoId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'livePhotoVideoId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'livePhotoVideoId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -3256,8 +3503,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -3269,23 +3517,28 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByStackId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'stackId', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByStackPrimaryAssetId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackPrimaryAssetId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'stackPrimaryAssetId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'stackPrimaryAssetId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByThumbhash( - {bool caseSensitive = true}) { + QueryBuilder distinctByThumbhash({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'thumbhash', caseSensitive: caseSensitive); }); @@ -3444,7 +3697,7 @@ extension AssetQueryProperty on QueryBuilder { } QueryBuilder - visibilityProperty() { + visibilityProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'visibility'); }); diff --git a/mobile/lib/entities/backup_album.entity.dart b/mobile/lib/entities/backup_album.entity.dart index 1e96c0452e..ad2a5d6718 100644 --- a/mobile/lib/entities/backup_album.entity.dart +++ b/mobile/lib/entities/backup_album.entity.dart @@ -14,21 +14,9 @@ class BackupAlbum { Id get isarId => fastHash(id); - BackupAlbum copyWith({ - String? id, - DateTime? lastBackup, - BackupSelection? selection, - }) { - return BackupAlbum( - id ?? this.id, - lastBackup ?? this.lastBackup, - selection ?? this.selection, - ); + BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) { + return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection); } } -enum BackupSelection { - none, - select, - exclude; -} +enum BackupSelection { none, select, exclude } diff --git a/mobile/lib/entities/backup_album.entity.g.dart b/mobile/lib/entities/backup_album.entity.g.dart index 23d00e43ca..ed98503119 100644 --- a/mobile/lib/entities/backup_album.entity.g.dart +++ b/mobile/lib/entities/backup_album.entity.g.dart @@ -17,11 +17,7 @@ const BackupAlbumSchema = CollectionSchema( name: r'BackupAlbum', id: 8308487201128361847, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ), + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), r'lastBackup': PropertySchema( id: 1, name: r'lastBackup', @@ -32,8 +28,9 @@ const BackupAlbumSchema = CollectionSchema( name: r'selection', type: IsarType.byte, enumMap: _BackupAlbumselectionEnumValueMap, - ) + ), }, + estimateSize: _backupAlbumEstimateSize, serialize: _backupAlbumSerialize, deserialize: _backupAlbumDeserialize, @@ -42,6 +39,7 @@ const BackupAlbumSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _backupAlbumGetId, getLinks: _backupAlbumGetLinks, attach: _backupAlbumAttach, @@ -96,9 +94,11 @@ P _backupAlbumDeserializeProp

( case 1: return (reader.readDateTime(offset)) as P; case 2: - return (_BackupAlbumselectionValueEnumMap[ - reader.readByteOrNull(offset)] ?? - BackupSelection.none) as P; + return (_BackupAlbumselectionValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + BackupSelection.none) + as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -124,7 +124,10 @@ List> _backupAlbumGetLinks(BackupAlbum object) { } void _backupAlbumAttach( - IsarCollection col, Id id, BackupAlbum object) {} + IsarCollection col, + Id id, + BackupAlbum object, +) {} extension BackupAlbumQueryWhereSort on QueryBuilder { @@ -138,17 +141,18 @@ extension BackupAlbumQueryWhereSort extension BackupAlbumQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder isarIdNotEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -171,8 +175,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdGreaterThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -181,8 +186,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdLessThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -197,12 +203,14 @@ extension BackupAlbumQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -214,11 +222,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -228,12 +238,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -243,12 +255,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -260,14 +274,16 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -276,11 +292,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -289,77 +307,82 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -368,11 +391,13 @@ extension BackupAlbumQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -383,125 +408,125 @@ extension BackupAlbumQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastBackupEqualTo(DateTime value) { + lastBackupEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lastBackup', value: value), + ); }); } QueryBuilder - lastBackupGreaterThan( - DateTime value, { - bool include = false, - }) { + lastBackupGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupLessThan( - DateTime value, { - bool include = false, - }) { + lastBackupLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupBetween( + lastBackupBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastBackup', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastBackup', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - selectionEqualTo(BackupSelection value) { + selectionEqualTo(BackupSelection value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'selection', value: value), + ); }); } QueryBuilder - selectionGreaterThan( - BackupSelection value, { - bool include = false, - }) { + selectionGreaterThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionLessThan( - BackupSelection value, { - bool include = false, - }) { + selectionLessThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionBetween( + selectionBetween( BackupSelection lower, BackupSelection upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'selection', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'selection', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -604,8 +629,9 @@ extension BackupAlbumQuerySortThenBy extension BackupAlbumQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -645,7 +671,7 @@ extension BackupAlbumQueryProperty } QueryBuilder - selectionProperty() { + selectionProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'selection'); }); diff --git a/mobile/lib/entities/duplicated_asset.entity.g.dart b/mobile/lib/entities/duplicated_asset.entity.g.dart index 8965d47c97..6cf08ad9cc 100644 --- a/mobile/lib/entities/duplicated_asset.entity.g.dart +++ b/mobile/lib/entities/duplicated_asset.entity.g.dart @@ -17,12 +17,9 @@ const DuplicatedAssetSchema = CollectionSchema( name: r'DuplicatedAsset', id: -2679334728174694496, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ) + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), }, + estimateSize: _duplicatedAssetEstimateSize, serialize: _duplicatedAssetSerialize, deserialize: _duplicatedAssetDeserialize, @@ -31,6 +28,7 @@ const DuplicatedAssetSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _duplicatedAssetGetId, getLinks: _duplicatedAssetGetLinks, attach: _duplicatedAssetAttach, @@ -62,9 +60,7 @@ DuplicatedAsset _duplicatedAssetDeserialize( List offsets, Map> allOffsets, ) { - final object = DuplicatedAsset( - reader.readString(offsets[0]), - ); + final object = DuplicatedAsset(reader.readString(offsets[0])); return object; } @@ -91,7 +87,10 @@ List> _duplicatedAssetGetLinks(DuplicatedAsset object) { } void _duplicatedAssetAttach( - IsarCollection col, Id id, DuplicatedAsset object) {} + IsarCollection col, + Id id, + DuplicatedAsset object, +) {} extension DuplicatedAssetQueryWhereSort on QueryBuilder { @@ -105,17 +104,16 @@ extension DuplicatedAssetQueryWhereSort extension DuplicatedAssetQueryWhere on QueryBuilder { QueryBuilder - isarIdEqualTo(Id isarId) { + isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -138,7 +136,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -147,7 +145,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -156,19 +154,21 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdBetween( + isarIdBetween( Id lowerIsarId, Id upperIsarId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -176,53 +176,52 @@ extension DuplicatedAssetQueryWhere extension DuplicatedAssetQueryFilter on QueryBuilder { QueryBuilder - idEqualTo( - String value, { - bool caseSensitive = true, - }) { + idEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idBetween( + idBetween( String lower, String upper, { bool includeLower = true, @@ -230,140 +229,141 @@ extension DuplicatedAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idMatches(String pattern, {bool caseSensitive = true}) { + idMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idIsEmpty() { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - idIsNotEmpty() { + idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdEqualTo(Id value) { + isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + isarIdLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdBetween( + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -410,7 +410,7 @@ extension DuplicatedAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -419,8 +419,9 @@ extension DuplicatedAssetQuerySortThenBy extension DuplicatedAssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/etag.entity.g.dart b/mobile/lib/entities/etag.entity.g.dart index afabca4aea..b1abba6bb7 100644 --- a/mobile/lib/entities/etag.entity.g.dart +++ b/mobile/lib/entities/etag.entity.g.dart @@ -22,17 +22,10 @@ const ETagSchema = CollectionSchema( name: r'assetCount', type: IsarType.long, ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ), - r'time': PropertySchema( - id: 2, - name: r'time', - type: IsarType.dateTime, - ) + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), + r'time': PropertySchema(id: 2, name: r'time', type: IsarType.dateTime), }, + estimateSize: _eTagEstimateSize, serialize: _eTagSerialize, deserialize: _eTagDeserialize, @@ -49,12 +42,13 @@ const ETagSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _eTagGetId, getLinks: _eTagGetLinks, attach: _eTagAttach, @@ -189,10 +183,9 @@ extension ETagQueryWhereSort on QueryBuilder { extension ETagQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -218,8 +211,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -227,8 +222,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -243,21 +240,22 @@ extension ETagQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -265,32 +263,40 @@ extension ETagQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -299,27 +305,27 @@ extension ETagQueryWhere on QueryBuilder { extension ETagQueryFilter on QueryBuilder { QueryBuilder assetCountIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetCount', value: value), + ); }); } @@ -328,11 +334,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -341,11 +349,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -356,13 +366,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -371,11 +383,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -385,12 +399,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -400,12 +416,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,14 +435,16 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -433,11 +453,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -446,60 +468,67 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -508,11 +537,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -521,11 +552,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -536,38 +569,39 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder timeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'time'), + ); }); } QueryBuilder timeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'time'), + ); }); } QueryBuilder timeEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'time', value: value), + ); }); } @@ -576,11 +610,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -589,11 +625,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -604,13 +642,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'time', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'time', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -714,8 +754,9 @@ extension ETagQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/ios_device_asset.entity.g.dart b/mobile/lib/entities/ios_device_asset.entity.g.dart index ffed338c91..8d8fec945b 100644 --- a/mobile/lib/entities/ios_device_asset.entity.g.dart +++ b/mobile/lib/entities/ios_device_asset.entity.g.dart @@ -17,17 +17,10 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'IOSDeviceAsset', id: -1671546753821948030, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), }, + estimateSize: _iOSDeviceAssetEstimateSize, serialize: _iOSDeviceAssetSerialize, deserialize: _iOSDeviceAssetDeserialize, @@ -44,7 +37,7 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -57,12 +50,13 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _iOSDeviceAssetGetId, getLinks: _iOSDeviceAssetGetLinks, attach: _iOSDeviceAssetAttach, @@ -128,7 +122,10 @@ List> _iOSDeviceAssetGetLinks(IOSDeviceAsset object) { } void _iOSDeviceAssetAttach( - IsarCollection col, Id id, IOSDeviceAsset object) {} + IsarCollection col, + Id id, + IOSDeviceAsset object, +) {} extension IOSDeviceAssetByIndex on IsarCollection { Future getById(String id) { @@ -179,8 +176,10 @@ extension IOSDeviceAssetByIndex on IsarCollection { return putAllByIndex(r'id', objects); } - List putAllByIdSync(List objects, - {bool saveLinks = true}) { + List putAllByIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'id', objects, saveLinks: saveLinks); } } @@ -197,17 +196,17 @@ extension IOSDeviceAssetQueryWhereSort extension IOSDeviceAssetQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -230,7 +229,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -239,7 +238,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -254,101 +253,120 @@ extension IOSDeviceAssetQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } QueryBuilder idNotEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } QueryBuilder hashEqualTo( - List hash) { + List hash, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -357,134 +375,97 @@ extension IOSDeviceAssetQueryWhere extension IOSDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -506,43 +487,45 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -554,141 +537,143 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - idIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - idIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - isarIdEqualTo(Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); - }); - } - - QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, + String pattern, { + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdBetween( + idIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); + }); + } + + QueryBuilder + isarIdEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); + }); + } + + QueryBuilder + isarIdGreaterThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdLessThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -735,7 +720,7 @@ extension IOSDeviceAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -750,8 +735,9 @@ extension IOSDeviceAssetQueryWhereDistinct }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/extensions/asset_extensions.dart b/mobile/lib/extensions/asset_extensions.dart index a5fa50983a..22d5d5030a 100644 --- a/mobile/lib/extensions/asset_extensions.dart +++ b/mobile/lib/extensions/asset_extensions.dart @@ -15,16 +15,10 @@ extension TZExtension on Asset { final location = getLocation(exifInfo!.timeZone!); dt = TZDateTime.from(dt, location); } on LocationNotFoundException { - RegExp re = RegExp( - r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', - caseSensitive: false, - ); + RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false); final m = re.firstMatch(exifInfo!.timeZone!); if (m != null) { - final duration = Duration( - hours: int.parse(m.group(1) ?? '0'), - minutes: int.parse(m.group(2) ?? '0'), - ); + final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0')); dt = dt.add(duration); return (dt, duration); } diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart index 95d2f74df8..541db7ccaf 100644 --- a/mobile/lib/extensions/collection_extensions.dart +++ b/mobile/lib/extensions/collection_extensions.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/hash.dart'; extension ListExtension on List { - List uniqueConsecutive({ - int Function(E a, E b)? compare, - void Function(E a, E b)? onDuplicate, - }) { + List uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) { compare ??= (E a, E b) => a == b ? 0 : 1; int i = 1, j = 1; for (; i < length; i++) { @@ -45,9 +42,7 @@ extension IntListExtension on Iterable { extension AssetListExtension on Iterable { /// Returns the assets that are already available in the Immich server - Iterable remoteOnly({ - void Function()? errorCallback, - }) { + Iterable remoteOnly({void Function()? errorCallback}) { final bool onlyRemote = every((e) => e.isRemote); if (!onlyRemote) { if (errorCallback != null) errorCallback(); @@ -58,10 +53,7 @@ extension AssetListExtension on Iterable { /// Returns the assets that are owned by the user passed to the [owner] param /// If [owner] is null, an empty list is returned - Iterable ownedOnly( - UserDto? owner, { - void Function()? errorCallback, - }) { + Iterable ownedOnly(UserDto? owner, {void Function()? errorCallback}) { if (owner == null) return []; final isarUserId = fastHash(owner.id); final bool onlyOwned = every((e) => e.ownerId == isarUserId); diff --git a/mobile/lib/extensions/datetime_extensions.dart b/mobile/lib/extensions/datetime_extensions.dart index 7e54980270..0bc95565a6 100644 --- a/mobile/lib/extensions/datetime_extensions.dart +++ b/mobile/lib/extensions/datetime_extensions.dart @@ -47,11 +47,7 @@ extension DateRangeFormatting on DateTime { /// - Date range of this year: "Mar 23-May 31" /// - Date range of other year: "Aug 28 - Sep 30, 2023" /// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022" - static String formatDateRange( - DateTime startDate, - DateTime endDate, - Locale? locale, - ) { + static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) { final now = DateTime.now(); final currentYear = now.year; final localeString = locale?.toString() ?? 'en_US'; diff --git a/mobile/lib/extensions/maplibrecontroller_extensions.dart b/mobile/lib/extensions/maplibrecontroller_extensions.dart index 42d5e2c1d0..e1f32a4d8c 100644 --- a/mobile/lib/extensions/maplibrecontroller_extensions.dart +++ b/mobile/lib/extensions/maplibrecontroller_extensions.dart @@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController { Future addGeoJSONSourceForMarkers(List markers) async { return addSource( MapUtils.defaultSourceId, - GeojsonSourceProperties( - data: MapUtils.generateGeoJsonForMarkers(markers.toList()), - ), + GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())), ); } @@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController { try { final ByteData bytes = await rootBundle.load("assets/location-pin.png"); await addImage("mapMarker", bytes.buffer.asUint8List()); - return addSymbol( - SymbolOptions( - geometry: centre, - iconImage: "mapMarker", - iconSize: 0.15, - iconAnchor: "bottom", - ), - ); + return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom")); } finally { // no-op } } - Future getBoundsFromPoint( - Point point, - double distance, - ) async { + Future getBoundsFromPoint(Point point, double distance) async { final southWestPx = Point(point.x - distance, point.y + distance); final northEastPx = Point(point.x + distance, point.y - distance); diff --git a/mobile/lib/extensions/scroll_extensions.dart b/mobile/lib/extensions/scroll_extensions.dart index 2752d0b77a..169032ff5d 100644 --- a/mobile/lib/extensions/scroll_extensions.dart +++ b/mobile/lib/extensions/scroll_extensions.dart @@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics { } @override - SpringDescription get spring => const SpringDescription( - mass: 1, - stiffness: 402.49984375, - damping: 40, - ); + SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40); } class FastClampingScrollPhysics extends ClampingScrollPhysics { @@ -27,12 +23,12 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics { @override SpringDescription get spring => const SpringDescription( - // When swiping between videos on Android, the placeholder of the first opened video - // can briefly be seen and cause a flicker effect if the video begins to initialize - // before the animation finishes - probably a bug in PhotoViewGallery's animation handling - // Making the animation faster is not just stylistic, but also helps to avoid this flicker - mass: 1, - stiffness: 1601.2499609375, - damping: 80, - ); + // When swiping between videos on Android, the placeholder of the first opened video + // can briefly be seen and cause a flicker effect if the video begins to initialize + // before the animation finishes - probably a bug in PhotoViewGallery's animation handling + // Making the animation faster is not just stylistic, but also helps to avoid this flicker + mass: 1, + stiffness: 1601.2499609375, + damping: 80, + ); } diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 65660c04ef..6cd6e1e4b4 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,10 +1,6 @@ extension StringExtension on String { String capitalize() { - return split(" ") - .map( - (str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1), - ) - .join(" "); + return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" "); } } diff --git a/mobile/lib/extensions/theme_extensions.dart b/mobile/lib/extensions/theme_extensions.dart index 6da8ac1fe6..332dc58fc0 100644 --- a/mobile/lib/extensions/theme_extensions.dart +++ b/mobile/lib/extensions/theme_extensions.dart @@ -7,16 +7,10 @@ extension ImmichColorSchemeExtensions on ColorScheme { extension ColorExtensions on Color { Color lighten({double amount = 0.1}) { - return Color.alphaBlend( - Colors.white.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.white.withValues(alpha: amount), this); } Color darken({double amount = 0.1}) { - return Color.alphaBlend( - Colors.black.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.black.withValues(alpha: amount), this); } } diff --git a/mobile/lib/extensions/translate_extensions.dart b/mobile/lib/extensions/translate_extensions.dart index d8a2810e79..cfd8c8cd1f 100644 --- a/mobile/lib/extensions/translate_extensions.dart +++ b/mobile/lib/extensions/translate_extensions.dart @@ -29,11 +29,7 @@ extension TextTranslateExtension on Text { } } -String _translateHelper( - BuildContext? context, - String key, [ - Map? args, -]) { +String _translateHelper(BuildContext? context, String key, [Map? args]) { if (key.isEmpty) { return ''; } diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart index 140af60de1..092fcc5859 100644 --- a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -11,88 +11,108 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i5; -typedef $$AssetFaceEntityTableCreateCompanionBuilder - = i1.AssetFaceEntityCompanion Function({ - required String id, - required String assetId, - i0.Value personId, - required int imageWidth, - required int imageHeight, - required int boundingBoxX1, - required int boundingBoxY1, - required int boundingBoxX2, - required int boundingBoxY2, - required String sourceType, -}); -typedef $$AssetFaceEntityTableUpdateCompanionBuilder - = i1.AssetFaceEntityCompanion Function({ - i0.Value id, - i0.Value assetId, - i0.Value personId, - i0.Value imageWidth, - i0.Value imageHeight, - i0.Value boundingBoxX1, - i0.Value boundingBoxY1, - i0.Value boundingBoxX2, - i0.Value boundingBoxY2, - i0.Value sourceType, -}); +typedef $$AssetFaceEntityTableCreateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, + }); -final class $$AssetFaceEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$AssetFaceEntityTable, i1.AssetFaceEntityData> { +final class $$AssetFaceEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData + > { $$AssetFaceEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('asset_face_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('person_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('asset_face_entity') .personId, - i4.ReadDatabaseContainer(db) - .resultSet('person_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('person_entity').id, + ), + ); i5.$$PersonEntityTableProcessedTableManager? get personId { final $_column = $_itemColumn('person_id'); if ($_column == null) return null; final manager = i5 .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('person_entity')) + ).resultSet('person_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_personIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -106,75 +126,96 @@ class $$AssetFaceEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get imageWidth => $composableBuilder( - column: $table.imageWidth, builder: (column) => i0.ColumnFilters(column)); + column: $table.imageWidth, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get imageHeight => $composableBuilder( - column: $table.imageHeight, - builder: (column) => i0.ColumnFilters(column)); + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get sourceType => $composableBuilder( - column: $table.sourceType, builder: (column) => i0.ColumnFilters(column)); + column: $table.sourceType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableFilterComposer get personId { final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -189,79 +230,97 @@ class $$AssetFaceEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get imageWidth => $composableBuilder( - column: $table.imageWidth, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get imageHeight => $composableBuilder( - column: $table.imageHeight, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get sourceType => $composableBuilder( - column: $table.sourceType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableOrderingComposer get personId { final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -279,88 +338,116 @@ class $$AssetFaceEntityTableAnnotationComposer $composableBuilder(column: $table.id, builder: (column) => column); i0.GeneratedColumn get imageWidth => $composableBuilder( - column: $table.imageWidth, builder: (column) => column); + column: $table.imageWidth, + builder: (column) => column, + ); i0.GeneratedColumn get imageHeight => $composableBuilder( - column: $table.imageHeight, builder: (column) => column); + column: $table.imageHeight, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, builder: (column) => column); + column: $table.boundingBoxX1, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, builder: (column) => column); + column: $table.boundingBoxY1, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, builder: (column) => column); + column: $table.boundingBoxX2, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, builder: (column) => column); + column: $table.boundingBoxY2, + builder: (column) => column, + ); i0.GeneratedColumn get sourceType => $composableBuilder( - column: $table.sourceType, builder: (column) => column); + column: $table.sourceType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableAnnotationComposer get personId { final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$AssetFaceEntityTable, - i1.AssetFaceEntityData, - i1.$$AssetFaceEntityTableFilterComposer, - i1.$$AssetFaceEntityTableOrderingComposer, - i1.$$AssetFaceEntityTableAnnotationComposer, - $$AssetFaceEntityTableCreateCompanionBuilder, - $$AssetFaceEntityTableUpdateCompanionBuilder, - (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), - i1.AssetFaceEntityData, - i0.PrefetchHooks Function({bool assetId, bool personId})> { +class $$AssetFaceEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + > { $$AssetFaceEntityTableTableManager( - i0.GeneratedDatabase db, i1.$AssetFaceEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$AssetFaceEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -369,66 +456,69 @@ class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1 .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value assetId = const i0.Value.absent(), - i0.Value personId = const i0.Value.absent(), - i0.Value imageWidth = const i0.Value.absent(), - i0.Value imageHeight = const i0.Value.absent(), - i0.Value boundingBoxX1 = const i0.Value.absent(), - i0.Value boundingBoxY1 = const i0.Value.absent(), - i0.Value boundingBoxX2 = const i0.Value.absent(), - i0.Value boundingBoxY2 = const i0.Value.absent(), - i0.Value sourceType = const i0.Value.absent(), - }) => - i1.AssetFaceEntityCompanion( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ), - createCompanionCallback: ({ - required String id, - required String assetId, - i0.Value personId = const i0.Value.absent(), - required int imageWidth, - required int imageHeight, - required int boundingBoxX1, - required int boundingBoxY1, - required int boundingBoxX2, - required int boundingBoxY2, - required String sourceType, - }) => - i1.AssetFaceEntityCompanion.insert( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: + ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$AssetFaceEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, personId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -439,52 +529,65 @@ class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$AssetFaceEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$AssetFaceEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (personId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.personId, - referencedTable: - i1.$$AssetFaceEntityTableReferences._personIdTable(db), - referencedColumn: i1.$$AssetFaceEntityTableReferences - ._personIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (personId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$AssetFaceEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$AssetFaceEntityTable, - i1.AssetFaceEntityData, - i1.$$AssetFaceEntityTableFilterComposer, - i1.$$AssetFaceEntityTableOrderingComposer, - i1.$$AssetFaceEntityTableAnnotationComposer, - $$AssetFaceEntityTableCreateCompanionBuilder, - $$AssetFaceEntityTableUpdateCompanionBuilder, - (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), - i1.AssetFaceEntityData, - i0.PrefetchHooks Function({bool assetId, bool personId})>; +typedef $$AssetFaceEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + >; class $AssetFaceEntityTable extends i2.AssetFaceEntity with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { @@ -495,81 +598,126 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _personIdMeta = - const i0.VerificationMeta('personId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _personIdMeta = const i0.VerificationMeta( + 'personId', + ); @override late final i0.GeneratedColumn personId = i0.GeneratedColumn( - 'person_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES person_entity (id) ON DELETE SET NULL')); - static const i0.VerificationMeta _imageWidthMeta = - const i0.VerificationMeta('imageWidth'); + 'person_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + static const i0.VerificationMeta _imageWidthMeta = const i0.VerificationMeta( + 'imageWidth', + ); @override late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( - 'image_width', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); - static const i0.VerificationMeta _imageHeightMeta = - const i0.VerificationMeta('imageHeight'); + 'image_width', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _imageHeightMeta = const i0.VerificationMeta( + 'imageHeight', + ); @override late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( - 'image_height', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'image_height', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxX1Meta = const i0.VerificationMeta('boundingBoxX1'); @override late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( - 'bounding_box_x1', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_x1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxY1Meta = const i0.VerificationMeta('boundingBoxY1'); @override late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( - 'bounding_box_y1', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_y1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxX2Meta = const i0.VerificationMeta('boundingBoxX2'); @override late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( - 'bounding_box_x2', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_x2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxY2Meta = const i0.VerificationMeta('boundingBoxY2'); @override late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( - 'bounding_box_y2', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); - static const i0.VerificationMeta _sourceTypeMeta = - const i0.VerificationMeta('sourceType'); + 'bounding_box_y2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _sourceTypeMeta = const i0.VerificationMeta( + 'sourceType', + ); @override late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( - 'source_type', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'source_type', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType - ]; + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -577,8 +725,9 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity static const String $name = 'asset_face_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -587,68 +736,87 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity context.missing(_idMeta); } if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('person_id')) { - context.handle(_personIdMeta, - personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta)); + context.handle( + _personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta), + ); } if (data.containsKey('image_width')) { context.handle( - _imageWidthMeta, - imageWidth.isAcceptableOrUnknown( - data['image_width']!, _imageWidthMeta)); + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown(data['image_width']!, _imageWidthMeta), + ); } else if (isInserting) { context.missing(_imageWidthMeta); } if (data.containsKey('image_height')) { context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, _imageHeightMeta, - imageHeight.isAcceptableOrUnknown( - data['image_height']!, _imageHeightMeta)); + ), + ); } else if (isInserting) { context.missing(_imageHeightMeta); } if (data.containsKey('bounding_box_x1')) { context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, _boundingBoxX1Meta, - boundingBoxX1.isAcceptableOrUnknown( - data['bounding_box_x1']!, _boundingBoxX1Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxX1Meta); } if (data.containsKey('bounding_box_y1')) { context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, _boundingBoxY1Meta, - boundingBoxY1.isAcceptableOrUnknown( - data['bounding_box_y1']!, _boundingBoxY1Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxY1Meta); } if (data.containsKey('bounding_box_x2')) { context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, _boundingBoxX2Meta, - boundingBoxX2.isAcceptableOrUnknown( - data['bounding_box_x2']!, _boundingBoxX2Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxX2Meta); } if (data.containsKey('bounding_box_y2')) { context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, _boundingBoxY2Meta, - boundingBoxY2.isAcceptableOrUnknown( - data['bounding_box_y2']!, _boundingBoxY2Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxY2Meta); } if (data.containsKey('source_type')) { context.handle( - _sourceTypeMeta, - sourceType.isAcceptableOrUnknown( - data['source_type']!, _sourceTypeMeta)); + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown(data['source_type']!, _sourceTypeMeta), + ); } else if (isInserting) { context.missing(_sourceTypeMeta); } @@ -661,26 +829,46 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.AssetFaceEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - personId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}person_id']), - imageWidth: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}image_width'])!, - imageHeight: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}image_height'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, boundingBoxX1: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, boundingBoxY1: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, boundingBoxX2: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, boundingBoxY2: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, - sourceType: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}source_type'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, ); } @@ -707,17 +895,18 @@ class AssetFaceEntityData extends i0.DataClass final int boundingBoxX2; final int boundingBoxY2; final String sourceType; - const AssetFaceEntityData( - {required this.id, - required this.assetId, - this.personId, - required this.imageWidth, - required this.imageHeight, - required this.boundingBoxX1, - required this.boundingBoxY1, - required this.boundingBoxX2, - required this.boundingBoxY2, - required this.sourceType}); + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -736,8 +925,10 @@ class AssetFaceEntityData extends i0.DataClass return map; } - factory AssetFaceEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory AssetFaceEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return AssetFaceEntityData( id: serializer.fromJson(json['id']), @@ -769,38 +960,40 @@ class AssetFaceEntityData extends i0.DataClass }; } - i1.AssetFaceEntityData copyWith( - {String? id, - String? assetId, - i0.Value personId = const i0.Value.absent(), - int? imageWidth, - int? imageHeight, - int? boundingBoxX1, - int? boundingBoxY1, - int? boundingBoxX2, - int? boundingBoxY2, - String? sourceType}) => - i1.AssetFaceEntityData( - id: id ?? this.id, - assetId: assetId ?? this.assetId, - personId: personId.present ? personId.value : this.personId, - imageWidth: imageWidth ?? this.imageWidth, - imageHeight: imageHeight ?? this.imageHeight, - boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, - boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, - boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, - boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, - sourceType: sourceType ?? this.sourceType, - ); + i1.AssetFaceEntityData copyWith({ + String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { return AssetFaceEntityData( id: data.id.present ? data.id.value : this.id, assetId: data.assetId.present ? data.assetId.value : this.assetId, personId: data.personId.present ? data.personId.value : this.personId, - imageWidth: - data.imageWidth.present ? data.imageWidth.value : this.imageWidth, - imageHeight: - data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, boundingBoxX1: data.boundingBoxX1.present ? data.boundingBoxX1.value : this.boundingBoxX1, @@ -813,8 +1006,9 @@ class AssetFaceEntityData extends i0.DataClass boundingBoxY2: data.boundingBoxY2.present ? data.boundingBoxY2.value : this.boundingBoxY2, - sourceType: - data.sourceType.present ? data.sourceType.value : this.sourceType, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, ); } @@ -837,16 +1031,17 @@ class AssetFaceEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType); + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -898,15 +1093,15 @@ class AssetFaceEntityCompanion required int boundingBoxX2, required int boundingBoxY2, required String sourceType, - }) : id = i0.Value(id), - assetId = i0.Value(assetId), - imageWidth = i0.Value(imageWidth), - imageHeight = i0.Value(imageHeight), - boundingBoxX1 = i0.Value(boundingBoxX1), - boundingBoxY1 = i0.Value(boundingBoxY1), - boundingBoxX2 = i0.Value(boundingBoxX2), - boundingBoxY2 = i0.Value(boundingBoxY2), - sourceType = i0.Value(sourceType); + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); static i0.Insertable custom({ i0.Expression? id, i0.Expression? assetId, @@ -933,17 +1128,18 @@ class AssetFaceEntityCompanion }); } - i1.AssetFaceEntityCompanion copyWith( - {i0.Value? id, - i0.Value? assetId, - i0.Value? personId, - i0.Value? imageWidth, - i0.Value? imageHeight, - i0.Value? boundingBoxX1, - i0.Value? boundingBoxY1, - i0.Value? boundingBoxX2, - i0.Value? boundingBoxY2, - i0.Value? sourceType}) { + i1.AssetFaceEntityCompanion copyWith({ + i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType, + }) { return i1.AssetFaceEntityCompanion( id: id ?? this.id, assetId: assetId ?? this.assetId, diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.dart b/mobile/lib/infrastructure/entities/device_asset.entity.dart index d8bfb2aa45..e3e4a0d4f4 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.dart @@ -16,21 +16,10 @@ class DeviceAssetEntity { final List hash; final DateTime modifiedTime; - const DeviceAssetEntity({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime}); - DeviceAsset toModel() => DeviceAsset( - assetId: assetId, - hash: Uint8List.fromList(hash), - modifiedTime: modifiedTime, - ); + DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime); - static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity( - assetId: dto.assetId, - hash: dto.hash, - modifiedTime: dto.modifiedTime, - ); + static DeviceAssetEntity fromDto(DeviceAsset dto) => + DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime); } diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart index a66f8288ef..87ae54ad40 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart @@ -17,22 +17,15 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'DeviceAssetEntity', id: 6967030785073446271, properties: { - r'assetId': PropertySchema( - id: 0, - name: r'assetId', - type: IsarType.string, - ), - r'hash': PropertySchema( - id: 1, - name: r'hash', - type: IsarType.byteList, - ), + r'assetId': PropertySchema(id: 0, name: r'assetId', type: IsarType.string), + r'hash': PropertySchema(id: 1, name: r'hash', type: IsarType.byteList), r'modifiedTime': PropertySchema( id: 2, name: r'modifiedTime', type: IsarType.dateTime, - ) + ), }, + estimateSize: _deviceAssetEntityEstimateSize, serialize: _deviceAssetEntitySerialize, deserialize: _deviceAssetEntityDeserialize, @@ -49,7 +42,7 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'assetId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -62,12 +55,13 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _deviceAssetEntityGetId, getLinks: _deviceAssetEntityGetLinks, attach: _deviceAssetEntityAttach, @@ -133,12 +127,16 @@ Id _deviceAssetEntityGetId(DeviceAssetEntity object) { } List> _deviceAssetEntityGetLinks( - DeviceAssetEntity object) { + DeviceAssetEntity object, +) { return []; } void _deviceAssetEntityAttach( - IsarCollection col, Id id, DeviceAssetEntity object) {} + IsarCollection col, + Id id, + DeviceAssetEntity object, +) {} extension DeviceAssetEntityByIndex on IsarCollection { Future getByAssetId(String assetId) { @@ -189,8 +187,10 @@ extension DeviceAssetEntityByIndex on IsarCollection { return putAllByIndex(r'assetId', objects); } - List putAllByAssetIdSync(List objects, - {bool saveLinks = true}) { + List putAllByAssetIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'assetId', objects, saveLinks: saveLinks); } } @@ -207,17 +207,14 @@ extension DeviceAssetEntityQueryWhereSort extension DeviceAssetEntityQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -240,7 +237,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -249,7 +246,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -258,108 +255,124 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - assetIdEqualTo(String assetId) { + assetIdEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'assetId', - value: [assetId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'assetId', value: [assetId]), + ); }); } QueryBuilder - assetIdNotEqualTo(String assetId) { + assetIdNotEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ); } }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -368,53 +381,56 @@ extension DeviceAssetEntityQueryWhere extension DeviceAssetEntityQueryFilter on QueryBuilder { QueryBuilder - assetIdEqualTo( - String value, { - bool caseSensitive = true, - }) { + assetIdEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdGreaterThan( + assetIdGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdLessThan( + assetIdLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdBetween( + assetIdBetween( String lower, String upper, { bool includeLower = true, @@ -422,216 +438,181 @@ extension DeviceAssetEntityQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdEndsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdContains(String value, {bool caseSensitive = true}) { + assetIdContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdMatches(String pattern, {bool caseSensitive = true}) { + assetIdMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'assetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'assetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdIsEmpty() { + assetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetId', value: ''), + ); }); } QueryBuilder - assetIdIsNotEmpty() { + assetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'assetId', value: ''), + ); }); } QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -649,114 +630,112 @@ extension DeviceAssetEntityQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - modifiedTimeEqualTo(DateTime value) { + modifiedTimeEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedTime', value: value), + ); }); } QueryBuilder - modifiedTimeGreaterThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeLessThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeBetween( + modifiedTimeBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedTime', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -770,28 +749,28 @@ extension DeviceAssetEntityQueryLinks extension DeviceAssetEntityQuerySortBy on QueryBuilder { QueryBuilder - sortByAssetId() { + sortByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - sortByAssetIdDesc() { + sortByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); } QueryBuilder - sortByModifiedTime() { + sortByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - sortByModifiedTimeDesc() { + sortByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -801,14 +780,14 @@ extension DeviceAssetEntityQuerySortBy extension DeviceAssetEntityQuerySortThenBy on QueryBuilder { QueryBuilder - thenByAssetId() { + thenByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - thenByAssetIdDesc() { + thenByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); @@ -821,21 +800,21 @@ extension DeviceAssetEntityQuerySortThenBy } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); } QueryBuilder - thenByModifiedTime() { + thenByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - thenByModifiedTimeDesc() { + thenByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -845,21 +824,21 @@ extension DeviceAssetEntityQuerySortThenBy extension DeviceAssetEntityQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByAssetId({bool caseSensitive = true}) { + distinctByAssetId({bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'assetId', caseSensitive: caseSensitive); }); } QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); } QueryBuilder - distinctByModifiedTime() { + distinctByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'modifiedTime'); }); @@ -887,7 +866,7 @@ extension DeviceAssetEntityQueryProperty } QueryBuilder - modifiedTimeProperty() { + modifiedTimeProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'modifiedTime'); }); diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index dae5486ab1..e8c7541349 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -52,47 +52,47 @@ class ExifInfo { }); static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo( - id: dto.assetId, - fileSize: dto.fileSize, - dateTimeOriginal: dto.dateTimeOriginal, - timeZone: dto.timeZone, - make: dto.make, - model: dto.model, - lens: dto.lens, - f: dto.f, - mm: dto.mm, - iso: dto.iso?.toInt(), - exposureSeconds: dto.exposureSeconds, - lat: dto.latitude, - long: dto.longitude, - city: dto.city, - state: dto.state, - country: dto.country, - description: dto.description, - orientation: dto.orientation, - ); + id: dto.assetId, + fileSize: dto.fileSize, + dateTimeOriginal: dto.dateTimeOriginal, + timeZone: dto.timeZone, + make: dto.make, + model: dto.model, + lens: dto.lens, + f: dto.f, + mm: dto.mm, + iso: dto.iso?.toInt(), + exposureSeconds: dto.exposureSeconds, + lat: dto.latitude, + long: dto.longitude, + city: dto.city, + state: dto.state, + country: dto.country, + description: dto.description, + orientation: dto.orientation, + ); domain.ExifInfo toDto() => domain.ExifInfo( - assetId: id, - fileSize: fileSize, - description: description, - orientation: orientation, - timeZone: timeZone, - dateTimeOriginal: dateTimeOriginal, - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - latitude: lat, - longitude: long, - city: city, - state: state, - country: country, - make: make, - model: model, - lens: lens, - f: f, - mm: mm, - iso: iso?.toInt(), - exposureSeconds: exposureSeconds, - ); + assetId: id, + fileSize: fileSize, + description: description, + orientation: orientation, + timeZone: timeZone, + dateTimeOriginal: dateTimeOriginal, + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + latitude: lat, + longitude: long, + city: city, + state: state, + country: country, + make: make, + model: model, + lens: lens, + f: f, + mm: mm, + iso: iso?.toInt(), + exposureSeconds: exposureSeconds, + ); } class RemoteExifEntity extends Table with DriftDefaultsMixin { @@ -148,24 +148,24 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { extension RemoteExifEntityDataDomainEx on RemoteExifEntityData { domain.ExifInfo toDto() => domain.ExifInfo( - fileSize: fileSize, - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - make: make, - model: model, - iso: iso, - city: city, - state: state, - country: country, - description: description, - orientation: orientation, - latitude: latitude, - longitude: longitude, - f: fNumber?.toDouble(), - mm: focalLength?.toDouble(), - lens: lens, - width: width?.toDouble(), - height: height?.toDouble(), - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - ); + fileSize: fileSize, + dateTimeOriginal: dateTimeOriginal, + timeZone: timeZone, + make: make, + model: model, + iso: iso, + city: city, + state: state, + country: country, + description: description, + orientation: orientation, + latitude: latitude, + longitude: longitude, + f: fNumber?.toDouble(), + mm: focalLength?.toDouble(), + lens: lens, + width: width?.toDouble(), + height: height?.toDouble(), + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + ); } diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index 10712948ea..d45d6e8ef6 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -8,86 +8,100 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift. as i3; import 'package:drift/internal/modular.dart' as i4; -typedef $$RemoteExifEntityTableCreateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - required String assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); -typedef $$RemoteExifEntityTableUpdateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - i0.Value assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); +typedef $$RemoteExifEntityTableCreateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + required String assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); +typedef $$RemoteExifEntityTableUpdateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + i0.Value assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); -final class $$RemoteExifEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$RemoteExifEntityTable, i1.RemoteExifEntityData> { +final class $$RemoteExifEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData + > { $$RemoteExifEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('remote_exif_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -101,93 +115,134 @@ class $$RemoteExifEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnFilters(column)); + column: $table.city, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnFilters(column)); + column: $table.state, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnFilters(column)); + column: $table.country, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnFilters(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnFilters(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnFilters(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnFilters(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.latitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get longitude => $composableBuilder( - column: $table.longitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.longitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnFilters(column)); + column: $table.iso, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnFilters(column)); + column: $table.make, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnFilters(column)); + column: $table.model, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnFilters(column)); + column: $table.lens, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnFilters(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnFilters(column)); + column: $table.rating, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnFilters(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -202,96 +257,135 @@ class $$RemoteExifEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnOrderings(column)); + column: $table.city, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnOrderings(column)); + column: $table.state, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnOrderings(column)); + column: $table.country, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnOrderings(column)); + column: $table.latitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get longitude => $composableBuilder( - column: $table.longitude, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.longitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnOrderings(column)); + column: $table.iso, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnOrderings(column)); + column: $table.make, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnOrderings(column)); + column: $table.model, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnOrderings(column)); + column: $table.lens, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnOrderings(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnOrderings(column)); + column: $table.rating, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -315,10 +409,14 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.country, builder: (column) => column); i0.GeneratedColumn get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, builder: (column) => column); + column: $table.dateTimeOriginal, + builder: (column) => column, + ); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get height => $composableBuilder(column: $table.height, builder: (column) => column); @@ -327,7 +425,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.width, builder: (column) => column); i0.GeneratedColumn get exposureTime => $composableBuilder( - column: $table.exposureTime, builder: (column) => column); + column: $table.exposureTime, + builder: (column) => column, + ); i0.GeneratedColumn get fNumber => $composableBuilder(column: $table.fNumber, builder: (column) => column); @@ -336,7 +436,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.fileSize, builder: (column) => column); i0.GeneratedColumn get focalLength => $composableBuilder( - column: $table.focalLength, builder: (column) => column); + column: $table.focalLength, + builder: (column) => column, + ); i0.GeneratedColumn get latitude => $composableBuilder(column: $table.latitude, builder: (column) => column); @@ -357,7 +459,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.lens, builder: (column) => column); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); i0.GeneratedColumn get timeZone => $composableBuilder(column: $table.timeZone, builder: (column) => column); @@ -366,48 +470,59 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.rating, builder: (column) => column); i0.GeneratedColumn get projectionType => $composableBuilder( - column: $table.projectionType, builder: (column) => column); + column: $table.projectionType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})> { +class $$RemoteExifEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + > { $$RemoteExifEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteExifEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteExifEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -416,115 +531,120 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< .$$RemoteExifEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteExifEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), - createCompanionCallback: ({ - required String assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion.insert( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), + createCompanionCallback: + ({ + required String assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion.insert( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteExifEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteExifEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -535,41 +655,50 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$RemoteExifEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$RemoteExifEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteExifEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})>; +typedef $$RemoteExifEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + >; class $RemoteExifEntityTable extends i2.RemoteExifEntity with i0.TableInfo<$RemoteExifEntityTable, i1.RemoteExifEntityData> { @@ -577,165 +706,277 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteExifEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _cityMeta = - const i0.VerificationMeta('city'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _cityMeta = const i0.VerificationMeta( + 'city', + ); @override late final i0.GeneratedColumn city = i0.GeneratedColumn( - 'city', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _stateMeta = - const i0.VerificationMeta('state'); + 'city', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stateMeta = const i0.VerificationMeta( + 'state', + ); @override late final i0.GeneratedColumn state = i0.GeneratedColumn( - 'state', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _countryMeta = - const i0.VerificationMeta('country'); + 'state', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _countryMeta = const i0.VerificationMeta( + 'country', + ); @override late final i0.GeneratedColumn country = i0.GeneratedColumn( - 'country', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _dateTimeOriginalMeta = const i0.VerificationMeta('dateTimeOriginal'); @override late final i0.GeneratedColumn dateTimeOriginal = - i0.GeneratedColumn('date_time_original', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + i0.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + i0.GeneratedColumn( + 'description', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _exposureTimeMeta = const i0.VerificationMeta('exposureTime'); @override late final i0.GeneratedColumn exposureTime = - i0.GeneratedColumn('exposure_time', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _fNumberMeta = - const i0.VerificationMeta('fNumber'); + i0.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fNumberMeta = const i0.VerificationMeta( + 'fNumber', + ); @override late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( - 'f_number', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _fileSizeMeta = - const i0.VerificationMeta('fileSize'); + 'f_number', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fileSizeMeta = const i0.VerificationMeta( + 'fileSize', + ); @override late final i0.GeneratedColumn fileSize = i0.GeneratedColumn( - 'file_size', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _focalLengthMeta = - const i0.VerificationMeta('focalLength'); + 'file_size', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _focalLengthMeta = const i0.VerificationMeta( + 'focalLength', + ); @override late final i0.GeneratedColumn focalLength = - i0.GeneratedColumn('focal_length', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _latitudeMeta = - const i0.VerificationMeta('latitude'); + i0.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta( + 'latitude', + ); @override late final i0.GeneratedColumn latitude = i0.GeneratedColumn( - 'latitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _longitudeMeta = - const i0.VerificationMeta('longitude'); + 'latitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta( + 'longitude', + ); @override late final i0.GeneratedColumn longitude = i0.GeneratedColumn( - 'longitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _isoMeta = const i0.VerificationMeta('iso'); @override late final i0.GeneratedColumn iso = i0.GeneratedColumn( - 'iso', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _makeMeta = - const i0.VerificationMeta('make'); + 'iso', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _makeMeta = const i0.VerificationMeta( + 'make', + ); @override late final i0.GeneratedColumn make = i0.GeneratedColumn( - 'make', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _modelMeta = - const i0.VerificationMeta('model'); + 'make', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _modelMeta = const i0.VerificationMeta( + 'model', + ); @override late final i0.GeneratedColumn model = i0.GeneratedColumn( - 'model', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _lensMeta = - const i0.VerificationMeta('lens'); + 'model', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _lensMeta = const i0.VerificationMeta( + 'lens', + ); @override late final i0.GeneratedColumn lens = i0.GeneratedColumn( - 'lens', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'lens', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = - i0.GeneratedColumn('orientation', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _timeZoneMeta = - const i0.VerificationMeta('timeZone'); + i0.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _timeZoneMeta = const i0.VerificationMeta( + 'timeZone', + ); @override late final i0.GeneratedColumn timeZone = i0.GeneratedColumn( - 'time_zone', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _ratingMeta = - const i0.VerificationMeta('rating'); + 'time_zone', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ratingMeta = const i0.VerificationMeta( + 'rating', + ); @override late final i0.GeneratedColumn rating = i0.GeneratedColumn( - 'rating', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _projectionTypeMeta = const i0.VerificationMeta('projectionType'); @override late final i0.GeneratedColumn projectionType = - i0.GeneratedColumn('projection_type', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -743,111 +984,162 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const String $name = 'remote_exif_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('city')) { context.handle( - _cityMeta, city.isAcceptableOrUnknown(data['city']!, _cityMeta)); + _cityMeta, + city.isAcceptableOrUnknown(data['city']!, _cityMeta), + ); } if (data.containsKey('state')) { context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); + _stateMeta, + state.isAcceptableOrUnknown(data['state']!, _stateMeta), + ); } if (data.containsKey('country')) { - context.handle(_countryMeta, - country.isAcceptableOrUnknown(data['country']!, _countryMeta)); + context.handle( + _countryMeta, + country.isAcceptableOrUnknown(data['country']!, _countryMeta), + ); } if (data.containsKey('date_time_original')) { context.handle( + _dateTimeOriginalMeta, + dateTimeOriginal.isAcceptableOrUnknown( + data['date_time_original']!, _dateTimeOriginalMeta, - dateTimeOriginal.isAcceptableOrUnknown( - data['date_time_original']!, _dateTimeOriginalMeta)); + ), + ); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('exposure_time')) { context.handle( + _exposureTimeMeta, + exposureTime.isAcceptableOrUnknown( + data['exposure_time']!, _exposureTimeMeta, - exposureTime.isAcceptableOrUnknown( - data['exposure_time']!, _exposureTimeMeta)); + ), + ); } if (data.containsKey('f_number')) { - context.handle(_fNumberMeta, - fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta)); + context.handle( + _fNumberMeta, + fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta), + ); } if (data.containsKey('file_size')) { - context.handle(_fileSizeMeta, - fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta)); + context.handle( + _fileSizeMeta, + fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta), + ); } if (data.containsKey('focal_length')) { context.handle( + _focalLengthMeta, + focalLength.isAcceptableOrUnknown( + data['focal_length']!, _focalLengthMeta, - focalLength.isAcceptableOrUnknown( - data['focal_length']!, _focalLengthMeta)); + ), + ); } if (data.containsKey('latitude')) { - context.handle(_latitudeMeta, - latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta)); + context.handle( + _latitudeMeta, + latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta), + ); } if (data.containsKey('longitude')) { - context.handle(_longitudeMeta, - longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta)); + context.handle( + _longitudeMeta, + longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta), + ); } if (data.containsKey('iso')) { context.handle( - _isoMeta, iso.isAcceptableOrUnknown(data['iso']!, _isoMeta)); + _isoMeta, + iso.isAcceptableOrUnknown(data['iso']!, _isoMeta), + ); } if (data.containsKey('make')) { context.handle( - _makeMeta, make.isAcceptableOrUnknown(data['make']!, _makeMeta)); + _makeMeta, + make.isAcceptableOrUnknown(data['make']!, _makeMeta), + ); } if (data.containsKey('model')) { context.handle( - _modelMeta, model.isAcceptableOrUnknown(data['model']!, _modelMeta)); + _modelMeta, + model.isAcceptableOrUnknown(data['model']!, _modelMeta), + ); } if (data.containsKey('lens')) { context.handle( - _lensMeta, lens.isAcceptableOrUnknown(data['lens']!, _lensMeta)); + _lensMeta, + lens.isAcceptableOrUnknown(data['lens']!, _lensMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } if (data.containsKey('time_zone')) { - context.handle(_timeZoneMeta, - timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta)); + context.handle( + _timeZoneMeta, + timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta), + ); } if (data.containsKey('rating')) { - context.handle(_ratingMeta, - rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta)); + context.handle( + _ratingMeta, + rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta), + ); } if (data.containsKey('projection_type')) { context.handle( + _projectionTypeMeta, + projectionType.isAcceptableOrUnknown( + data['projection_type']!, _projectionTypeMeta, - projectionType.isAcceptableOrUnknown( - data['projection_type']!, _projectionTypeMeta)); + ), + ); } return context; } @@ -855,55 +1147,100 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity @override Set get $primaryKey => {assetId}; @override - i1.RemoteExifEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteExifEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, - data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), exposureTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}rating']), + i0.DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}rating'], + ), projectionType: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}projection_type']), + i0.DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -942,29 +1279,30 @@ class RemoteExifEntityData extends i0.DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1035,16 +1373,19 @@ class RemoteExifEntityData extends i0.DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -1093,57 +1434,57 @@ class RemoteExifEntityData extends i0.DataClass }; } - i1.RemoteExifEntityData copyWith( - {String? assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent()}) => - i1.RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + i1.RemoteExifEntityData copyWith({ + String? assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(i1.RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -1153,8 +1494,9 @@ class RemoteExifEntityData extends i0.DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -1162,16 +1504,18 @@ class RemoteExifEntityData extends i0.DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -1211,29 +1555,29 @@ class RemoteExifEntityData extends i0.DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -1384,29 +1728,30 @@ class RemoteExifEntityCompanion }); } - i1.RemoteExifEntityCompanion copyWith( - {i0.Value? assetId, - i0.Value? city, - i0.Value? state, - i0.Value? country, - i0.Value? dateTimeOriginal, - i0.Value? description, - i0.Value? height, - i0.Value? width, - i0.Value? exposureTime, - i0.Value? fNumber, - i0.Value? fileSize, - i0.Value? focalLength, - i0.Value? latitude, - i0.Value? longitude, - i0.Value? iso, - i0.Value? make, - i0.Value? model, - i0.Value? lens, - i0.Value? orientation, - i0.Value? timeZone, - i0.Value? rating, - i0.Value? projectionType}) { + i1.RemoteExifEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? city, + i0.Value? state, + i0.Value? country, + i0.Value? dateTimeOriginal, + i0.Value? description, + i0.Value? height, + i0.Value? width, + i0.Value? exposureTime, + i0.Value? fNumber, + i0.Value? fileSize, + i0.Value? focalLength, + i0.Value? latitude, + i0.Value? longitude, + i0.Value? iso, + i0.Value? make, + i0.Value? model, + i0.Value? lens, + i0.Value? orientation, + i0.Value? timeZone, + i0.Value? rating, + i0.Value? projectionType, + }) { return i1.RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, diff --git a/mobile/lib/infrastructure/entities/exif.entity.g.dart b/mobile/lib/infrastructure/entities/exif.entity.g.dart index 989338abff..d2f9ebda27 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.g.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.g.dart @@ -17,16 +17,8 @@ const ExifInfoSchema = CollectionSchema( name: r'ExifInfo', id: -2409260054350835217, properties: { - r'city': PropertySchema( - id: 0, - name: r'city', - type: IsarType.string, - ), - r'country': PropertySchema( - id: 1, - name: r'country', - type: IsarType.string, - ), + r'city': PropertySchema(id: 0, name: r'city', type: IsarType.string), + r'country': PropertySchema(id: 1, name: r'country', type: IsarType.string), r'dateTimeOriginal': PropertySchema( id: 2, name: r'dateTimeOriginal', @@ -42,67 +34,28 @@ const ExifInfoSchema = CollectionSchema( name: r'exposureSeconds', type: IsarType.float, ), - r'f': PropertySchema( - id: 5, - name: r'f', - type: IsarType.float, - ), - r'fileSize': PropertySchema( - id: 6, - name: r'fileSize', - type: IsarType.long, - ), - r'iso': PropertySchema( - id: 7, - name: r'iso', - type: IsarType.int, - ), - r'lat': PropertySchema( - id: 8, - name: r'lat', - type: IsarType.float, - ), - r'lens': PropertySchema( - id: 9, - name: r'lens', - type: IsarType.string, - ), - r'long': PropertySchema( - id: 10, - name: r'long', - type: IsarType.float, - ), - r'make': PropertySchema( - id: 11, - name: r'make', - type: IsarType.string, - ), - r'mm': PropertySchema( - id: 12, - name: r'mm', - type: IsarType.float, - ), - r'model': PropertySchema( - id: 13, - name: r'model', - type: IsarType.string, - ), + r'f': PropertySchema(id: 5, name: r'f', type: IsarType.float), + r'fileSize': PropertySchema(id: 6, name: r'fileSize', type: IsarType.long), + r'iso': PropertySchema(id: 7, name: r'iso', type: IsarType.int), + r'lat': PropertySchema(id: 8, name: r'lat', type: IsarType.float), + r'lens': PropertySchema(id: 9, name: r'lens', type: IsarType.string), + r'long': PropertySchema(id: 10, name: r'long', type: IsarType.float), + r'make': PropertySchema(id: 11, name: r'make', type: IsarType.string), + r'mm': PropertySchema(id: 12, name: r'mm', type: IsarType.float), + r'model': PropertySchema(id: 13, name: r'model', type: IsarType.string), r'orientation': PropertySchema( id: 14, name: r'orientation', type: IsarType.string, ), - r'state': PropertySchema( - id: 15, - name: r'state', - type: IsarType.string, - ), + r'state': PropertySchema(id: 15, name: r'state', type: IsarType.string), r'timeZone': PropertySchema( id: 16, name: r'timeZone', type: IsarType.string, - ) + ), }, + estimateSize: _exifInfoEstimateSize, serialize: _exifInfoSerialize, deserialize: _exifInfoDeserialize, @@ -111,6 +64,7 @@ const ExifInfoSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _exifInfoGetId, getLinks: _exifInfoGetLinks, attach: _exifInfoAttach, @@ -301,10 +255,7 @@ extension ExifInfoQueryWhereSort on QueryBuilder { extension ExifInfoQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -330,8 +281,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -339,8 +292,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -355,12 +310,14 @@ extension ExifInfoQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -369,17 +326,17 @@ extension ExifInfoQueryFilter on QueryBuilder { QueryBuilder cityIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'city'), + ); }); } QueryBuilder cityIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'city'), + ); }); } @@ -388,11 +345,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -402,12 +361,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,12 +378,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -434,14 +397,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'city', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'city', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -450,11 +415,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -463,69 +430,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'city', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'city', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'city', value: ''), + ); }); } QueryBuilder cityIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'city', value: ''), + ); }); } QueryBuilder countryIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'country'), + ); }); } QueryBuilder countryIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'country'), + ); }); } @@ -534,11 +507,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -548,12 +523,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -563,12 +540,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -580,14 +559,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'country', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'country', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -596,11 +577,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -609,144 +592,149 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'country', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'country', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'country', value: ''), + ); }); } QueryBuilder countryIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'country', value: ''), + ); }); } QueryBuilder - dateTimeOriginalIsNull() { + dateTimeOriginalIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalIsNotNull() { + dateTimeOriginalIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalEqualTo(DateTime? value) { + dateTimeOriginalEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'dateTimeOriginal', value: value), + ); }); } QueryBuilder - dateTimeOriginalGreaterThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalGreaterThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalLessThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalBetween( + dateTimeOriginalBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'dateTimeOriginal', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'dateTimeOriginal', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder - descriptionIsNotNull() { + descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -755,27 +743,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - descriptionGreaterThan( + descriptionGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -785,12 +777,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -802,14 +796,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -818,11 +814,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -831,123 +829,135 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder - descriptionIsNotEmpty() { + descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder - exposureSecondsIsNull() { + exposureSecondsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsIsNotNull() { + exposureSecondsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsEqualTo( - double? value, { - double epsilon = Query.epsilon, - }) { + exposureSecondsEqualTo(double? value, {double epsilon = Query.epsilon}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsGreaterThan( + exposureSecondsGreaterThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsLessThan( + exposureSecondsLessThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsBetween( + exposureSecondsBetween( double? lower, double? upper, { bool includeLower = true, @@ -955,30 +965,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'exposureSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'exposureSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'f'), + ); }); } QueryBuilder fIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'f'), + ); }); } @@ -987,11 +1000,9 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'f', value: value, epsilon: epsilon), + ); }); } @@ -1001,12 +1012,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1016,12 +1030,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1033,40 +1050,43 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'f', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'f', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fileSizeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileSize', value: value), + ); }); } @@ -1075,11 +1095,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1088,11 +1110,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1103,38 +1127,39 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileSize', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileSize', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'id'), + ); }); } QueryBuilder idIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'id'), + ); }); } QueryBuilder idEqualTo(Id? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1143,11 +1168,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1156,11 +1183,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1171,39 +1200,41 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isoIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'iso'), + ); }); } QueryBuilder isoIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'iso'), + ); }); } QueryBuilder isoEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'iso', value: value), + ); }); } @@ -1212,11 +1243,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1225,11 +1258,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1240,29 +1275,31 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'iso', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'iso', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder latIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lat'), + ); }); } QueryBuilder latIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lat'), + ); }); } @@ -1271,11 +1308,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1285,12 +1325,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1300,12 +1343,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1317,30 +1363,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lat', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lat', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder lensIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lens'), + ); }); } QueryBuilder lensIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lens'), + ); }); } @@ -1349,11 +1398,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1363,12 +1414,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1378,12 +1431,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1395,14 +1450,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lens', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lens', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1411,11 +1468,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1424,69 +1483,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'lens', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'lens', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lens', value: ''), + ); }); } QueryBuilder lensIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'lens', value: ''), + ); }); } QueryBuilder longIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'long'), + ); }); } QueryBuilder longIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'long'), + ); }); } @@ -1495,11 +1560,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1509,12 +1577,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1524,12 +1595,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1541,30 +1615,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'long', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'long', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder makeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'make'), + ); }); } QueryBuilder makeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'make'), + ); }); } @@ -1573,11 +1650,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1587,12 +1666,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1602,12 +1683,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1619,14 +1702,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'make', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'make', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1635,11 +1720,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1648,69 +1735,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'make', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'make', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'make', value: ''), + ); }); } QueryBuilder makeIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'make', value: ''), + ); }); } QueryBuilder mmIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'mm'), + ); }); } QueryBuilder mmIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'mm'), + ); }); } @@ -1719,11 +1812,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1733,12 +1829,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1748,12 +1847,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1765,30 +1867,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'mm', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'mm', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder modelIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'model'), + ); }); } QueryBuilder modelIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'model'), + ); }); } @@ -1797,11 +1902,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1811,12 +1918,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1826,12 +1935,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1843,14 +1954,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'model', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'model', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1859,11 +1972,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1872,70 +1987,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'model', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'model', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'model', value: ''), + ); }); } QueryBuilder modelIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'model', value: ''), + ); }); } QueryBuilder orientationIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'orientation'), + ); }); } QueryBuilder - orientationIsNotNull() { + orientationIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'orientation'), + ); }); } @@ -1944,27 +2065,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - orientationGreaterThan( + orientationGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1974,12 +2099,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1991,14 +2118,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'orientation', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'orientation', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2007,11 +2136,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2020,70 +2151,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'orientation', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'orientation', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'orientation', value: ''), + ); }); } QueryBuilder - orientationIsNotEmpty() { + orientationIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'orientation', value: ''), + ); }); } QueryBuilder stateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'state'), + ); }); } QueryBuilder stateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'state'), + ); }); } @@ -2092,11 +2229,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2106,12 +2245,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2121,12 +2262,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2138,14 +2281,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'state', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'state', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2154,11 +2299,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2167,69 +2314,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'state', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'state', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'state', value: ''), + ); }); } QueryBuilder stateIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'state', value: ''), + ); }); } QueryBuilder timeZoneIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'timeZone'), + ); }); } QueryBuilder timeZoneIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'timeZone'), + ); }); } @@ -2238,11 +2391,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2252,12 +2407,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2267,12 +2424,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2284,14 +2443,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'timeZone', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'timeZone', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,11 +2461,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2313,53 +2476,59 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timeZone', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'timeZone', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'timeZone', value: ''), + ); }); } QueryBuilder timeZoneIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'timeZone', value: ''), + ); }); } } @@ -2797,15 +2966,17 @@ extension ExifInfoQuerySortThenBy extension ExifInfoQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByCity( - {bool caseSensitive = true}) { + QueryBuilder distinctByCity({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'city', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByCountry( - {bool caseSensitive = true}) { + QueryBuilder distinctByCountry({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'country', caseSensitive: caseSensitive); }); @@ -2817,8 +2988,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -2854,8 +3026,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByLens( - {bool caseSensitive = true}) { + QueryBuilder distinctByLens({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'lens', caseSensitive: caseSensitive); }); @@ -2867,8 +3040,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByMake( - {bool caseSensitive = true}) { + QueryBuilder distinctByMake({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'make', caseSensitive: caseSensitive); }); @@ -2880,29 +3054,33 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByModel( - {bool caseSensitive = true}) { + QueryBuilder distinctByModel({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'model', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByOrientation( - {bool caseSensitive = true}) { + QueryBuilder distinctByOrientation({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'orientation', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByState( - {bool caseSensitive = true}) { + QueryBuilder distinctByState({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'state', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByTimeZone( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimeZone({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'timeZone', caseSensitive: caseSensitive); }); @@ -2930,7 +3108,7 @@ extension ExifInfoQueryProperty } QueryBuilder - dateTimeOriginalProperty() { + dateTimeOriginalProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'dateTimeOriginal'); }); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index 06f65e25d8..5be349c8e0 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -8,24 +8,24 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAlbumEntityTableCreateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value updatedAt, - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); -typedef $$LocalAlbumEntityTableUpdateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value updatedAt, - i0.Value backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); +typedef $$LocalAlbumEntityTableCreateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value updatedAt, + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); +typedef $$LocalAlbumEntityTableUpdateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value updatedAt, + i0.Value backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); class $$LocalAlbumEntityTableFilterComposer extends i0.Composer { @@ -37,25 +37,35 @@ class $$LocalAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnFilters(column)); + column: $table.marker_, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAlbumEntityTableOrderingComposer @@ -68,25 +78,34 @@ class $$LocalAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.backupSelection, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnOrderings(column)); + column: $table.marker_, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAlbumEntityTableAnnotationComposer @@ -108,35 +127,47 @@ class $$LocalAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumnWithTypeConverter - get backupSelection => $composableBuilder( - column: $table.backupSelection, builder: (column) => column); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => column, + ); i0.GeneratedColumn get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, builder: (column) => column); + column: $table.isIosSharedAlbum, + builder: (column) => column, + ); i0.GeneratedColumn get marker_ => $composableBuilder(column: $table.marker_, builder: (column) => column); } -class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData, + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + > { $$LocalAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -145,63 +176,71 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< .$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value backupSelection = - const i0.Value.absent(), - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value updatedAt = const i0.Value.absent(), - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion.insert( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value backupSelection = + const i0.Value.absent(), + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value updatedAt = const i0.Value.absent(), + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion.insert( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()>; + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + >; class $LocalAlbumEntityTable extends i3.LocalAlbumEntity with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> { @@ -212,51 +251,86 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); @override late final i0.GeneratedColumnWithTypeConverter - backupSelection = i0.GeneratedColumn( - 'backup_selection', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAlbumEntityTable.$converterbackupSelection); + backupSelection = + i0.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$LocalAlbumEntityTable.$converterbackupSelection, + ); static const i0.VerificationMeta _isIosSharedAlbumMeta = const i0.VerificationMeta('isIosSharedAlbum'); @override late final i0.GeneratedColumn isIosSharedAlbum = - i0.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _marker_Meta = - const i0.VerificationMeta('marker_'); + i0.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta( + 'marker_', + ); @override late final i0.GeneratedColumn marker_ = i0.GeneratedColumn( - 'marker', aliasedName, true, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -264,8 +338,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const String $name = 'local_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -275,23 +350,32 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('is_ios_shared_album')) { context.handle( + _isIosSharedAlbumMeta, + isIosSharedAlbum.isAcceptableOrUnknown( + data['is_ios_shared_album']!, _isIosSharedAlbumMeta, - isIosSharedAlbum.isAcceptableOrUnknown( - data['is_ios_shared_album']!, _isIosSharedAlbumMeta)); + ), + ); } if (data.containsKey('marker')) { - context.handle(_marker_Meta, - marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta)); + context.handle( + _marker_Meta, + marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta), + ); } return context; } @@ -299,23 +383,39 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity @override Set get $primaryKey => {id}; @override - i1.LocalAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection - .fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int, - data['${effectivePrefix}backup_selection'])!), + .fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + ), isIosSharedAlbum: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -325,9 +425,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } static i0.JsonTypeConverter2 - $converterbackupSelection = - const i0.EnumIndexConverter( - i2.BackupSelection.values); + $converterbackupSelection = const i0.EnumIndexConverter( + i2.BackupSelection.values, + ); @override bool get withoutRowId => true; @override @@ -342,13 +442,14 @@ class LocalAlbumEntityData extends i0.DataClass final i2.BackupSelection backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -356,9 +457,11 @@ class LocalAlbumEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['updated_at'] = i0.Variable(updatedAt); { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection, + ), + ); } map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum); if (!nullToAbsent || marker_ != null) { @@ -367,8 +470,10 @@ class LocalAlbumEntityData extends i0.DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -387,29 +492,31 @@ class LocalAlbumEntityData extends i0.DataClass 'id': serializer.toJson(id), 'name': serializer.toJson(name), 'updatedAt': serializer.toJson(updatedAt), - 'backupSelection': serializer.toJson(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toJson(backupSelection)), + 'backupSelection': serializer.toJson( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toJson( + backupSelection, + ), + ), 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), 'marker_': serializer.toJson(marker_), }; } - i1.LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - i2.BackupSelection? backupSelection, - bool? isIosSharedAlbum, - i0.Value marker_ = const i0.Value.absent()}) => - i1.LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + i1.LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + i2.BackupSelection? backupSelection, + bool? isIosSharedAlbum, + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -440,7 +547,13 @@ class LocalAlbumEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -476,9 +589,9 @@ class LocalAlbumEntityCompanion required i2.BackupSelection backupSelection, this.isIosSharedAlbum = const i0.Value.absent(), this.marker_ = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - backupSelection = i0.Value(backupSelection); + }) : id = i0.Value(id), + name = i0.Value(name), + backupSelection = i0.Value(backupSelection); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -497,13 +610,14 @@ class LocalAlbumEntityCompanion }); } - i1.LocalAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? updatedAt, - i0.Value? backupSelection, - i0.Value? isIosSharedAlbum, - i0.Value? marker_}) { + i1.LocalAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? updatedAt, + i0.Value? backupSelection, + i0.Value? isIosSharedAlbum, + i0.Value? marker_, + }) { return i1.LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -527,9 +641,11 @@ class LocalAlbumEntityCompanion map['updated_at'] = i0.Variable(updatedAt.value); } if (backupSelection.present) { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection.value)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection.value, + ), + ); } if (isIosSharedAlbum.present) { map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum.value); diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart index e8f94fa74b..78da361f62 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' as i5; -typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData> { +final class $$LocalAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { $$LocalAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('local_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_asset_entity').id, + ), + ); i3.$$LocalAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$LocalAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_asset_entity')) + ).resultSet('local_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('local_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_album_entity').id, + ), + ); i5.$$LocalAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$LocalAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_album_entity')) + ).resultSet('local_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$LocalAlbumAssetEntityTableFilterComposer }); i3.$$LocalAssetEntityTableFilterComposer get assetId { final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableFilterComposer get albumId { final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$LocalAlbumAssetEntityTableOrderingComposer i3.$$LocalAssetEntityTableOrderingComposer get assetId { final i3.$$LocalAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableOrderingComposer get albumId { final i5.$$LocalAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$LocalAlbumAssetEntityTableAnnotationComposer i3.$$LocalAssetEntityTableAnnotationComposer get assetId { final i3.$$LocalAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableAnnotationComposer get albumId { final i5.$$LocalAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$LocalAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableReferences, + ), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$LocalAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$LocalAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$LocalAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$LocalAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.LocalAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.LocalAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$LocalAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$LocalAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,104 @@ class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$LocalAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableReferences - ), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; +typedef $$LocalAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity with - i0 - .TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> { + i0.TableInfo< + $LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +486,24 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity static const String $name = 'local_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +513,20 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +545,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +557,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +582,8 @@ class LocalAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - i1.LocalAlbumAssetEntityCompanion data) { + i1.LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +620,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +632,10 @@ class LocalAlbumAssetEntityCompanion }); } - i1.LocalAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.LocalAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 204d5d6a80..e5519cfacf 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -22,17 +22,17 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { extension LocalAssetEntityDataDomainEx on LocalAssetEntityData { LocalAsset toDto() => LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - remoteId: null, - orientation: orientation, - ); + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + remoteId: null, + orientation: orientation, + ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index e9c5961aa5..329401d5db 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -8,34 +8,34 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAssetEntityTableCreateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); -typedef $$LocalAssetEntityTableUpdateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); +typedef $$LocalAssetEntityTableCreateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); +typedef $$LocalAssetEntityTableUpdateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); class $$LocalAssetEntityTableFilterComposer extends i0.Composer { @@ -47,41 +47,60 @@ class $$LocalAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAssetEntityTableOrderingComposer @@ -94,42 +113,59 @@ class $$LocalAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAssetEntityTableAnnotationComposer @@ -160,7 +196,9 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -169,31 +207,43 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); } -class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData, + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + > { $$LocalAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -202,84 +252,94 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< .$$LocalAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()>; -i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + >; +i0.Index get idxLocalAssetChecksum => i0.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', +); class $LocalAssetEntityTable extends i3.LocalAssetEntity with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> { @@ -287,95 +347,146 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LocalAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = i0.GeneratedColumn( - 'orientation', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i4.Constant(0)); + 'orientation', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -383,37 +494,51 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity static const String $name = 'local_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -421,20 +546,25 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } return context; } @@ -442,33 +572,58 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity @override Set get $primaryKey => {id}; @override - i1.LocalAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$LocalAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}orientation'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -498,25 +653,27 @@ class LocalAssetEntityData extends i0.DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -538,13 +695,16 @@ class LocalAssetEntityData extends i0.DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$LocalAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$LocalAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -561,8 +721,9 @@ class LocalAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$LocalAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$LocalAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -575,33 +736,33 @@ class LocalAssetEntityData extends i0.DataClass }; } - i1.LocalAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - i0.Value checksum = const i0.Value.absent(), - bool? isFavorite, - int? orientation}) => - i1.LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + i1.LocalAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + i0.Value checksum = const i0.Value.absent(), + bool? isFavorite, + int? orientation, + }) => i1.LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -615,10 +776,12 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -641,8 +804,19 @@ class LocalAssetEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -698,9 +872,9 @@ class LocalAssetEntityCompanion this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.orientation = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -729,18 +903,19 @@ class LocalAssetEntityCompanion }); } - i1.LocalAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? orientation}) { + i1.LocalAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? orientation, + }) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -764,7 +939,8 @@ class LocalAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type.value)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart index 9300cf15c5..02fa817a08 100644 --- a/mobile/lib/infrastructure/entities/log.entity.g.dart +++ b/mobile/lib/infrastructure/entities/log.entity.g.dart @@ -32,23 +32,16 @@ const LoggerMessageSchema = CollectionSchema( name: r'createdAt', type: IsarType.dateTime, ), - r'details': PropertySchema( - id: 3, - name: r'details', - type: IsarType.string, - ), + r'details': PropertySchema(id: 3, name: r'details', type: IsarType.string), r'level': PropertySchema( id: 4, name: r'level', type: IsarType.byte, enumMap: _LoggerMessagelevelEnumValueMap, ), - r'message': PropertySchema( - id: 5, - name: r'message', - type: IsarType.string, - ) + r'message': PropertySchema(id: 5, name: r'message', type: IsarType.string), }, + estimateSize: _loggerMessageEstimateSize, serialize: _loggerMessageSerialize, deserialize: _loggerMessageDeserialize, @@ -57,6 +50,7 @@ const LoggerMessageSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _loggerMessageGetId, getLinks: _loggerMessageGetLinks, attach: _loggerMessageAttach, @@ -116,7 +110,8 @@ LoggerMessage _loggerMessageDeserialize( context2: reader.readStringOrNull(offsets[1]), createdAt: reader.readDateTime(offsets[2]), details: reader.readStringOrNull(offsets[3]), - level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? + level: + _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? LogLevel.info, message: reader.readString(offsets[5]), ); @@ -140,7 +135,8 @@ P _loggerMessageDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 4: return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? - LogLevel.info) as P; + LogLevel.info) + as P; case 5: return (reader.readString(offset)) as P; default: @@ -182,7 +178,10 @@ List> _loggerMessageGetLinks(LoggerMessage object) { } void _loggerMessageAttach( - IsarCollection col, Id id, LoggerMessage object) {} + IsarCollection col, + Id id, + LoggerMessage object, +) {} extension LoggerMessageQueryWhereSort on QueryBuilder { @@ -196,17 +195,16 @@ extension LoggerMessageQueryWhereSort extension LoggerMessageQueryWhere on QueryBuilder { QueryBuilder idEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder idNotEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -229,8 +227,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idGreaterThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -239,8 +238,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idLessThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -255,12 +255,14 @@ extension LoggerMessageQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -268,71 +270,74 @@ extension LoggerMessageQueryWhere extension LoggerMessageQueryFilter on QueryBuilder { QueryBuilder - context1IsNull() { + context1IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context1'), + ); }); } QueryBuilder - context1IsNotNull() { + context1IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context1'), + ); }); } QueryBuilder - context1EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context1EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1GreaterThan( + context1GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1LessThan( + context1LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Between( + context1Between( String? lower, String? upper, { bool includeLower = true, @@ -340,153 +345,158 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context1', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context1', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1StartsWith( - String value, { - bool caseSensitive = true, - }) { + context1StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1EndsWith( - String value, { - bool caseSensitive = true, - }) { + context1EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Contains(String value, {bool caseSensitive = true}) { + context1Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Matches(String pattern, {bool caseSensitive = true}) { + context1Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context1', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context1', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1IsEmpty() { + context1IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context1', value: ''), + ); }); } QueryBuilder - context1IsNotEmpty() { + context1IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context1', value: ''), + ); }); } QueryBuilder - context2IsNull() { + context2IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context2'), + ); }); } QueryBuilder - context2IsNotNull() { + context2IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context2'), + ); }); } QueryBuilder - context2EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context2EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2GreaterThan( + context2GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2LessThan( + context2LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Between( + context2Between( String? lower, String? upper, { bool includeLower = true, @@ -494,209 +504,213 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context2', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context2', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2StartsWith( - String value, { - bool caseSensitive = true, - }) { + context2StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2EndsWith( - String value, { - bool caseSensitive = true, - }) { + context2EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Contains(String value, {bool caseSensitive = true}) { + context2Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Matches(String pattern, {bool caseSensitive = true}) { + context2Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context2', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context2', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2IsEmpty() { + context2IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context2', value: ''), + ); }); } QueryBuilder - context2IsNotEmpty() { + context2IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context2', value: ''), + ); }); } QueryBuilder - createdAtEqualTo(DateTime value) { + createdAtEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } QueryBuilder - createdAtGreaterThan( - DateTime value, { - bool include = false, - }) { + createdAtGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtLessThan( - DateTime value, { - bool include = false, - }) { + createdAtLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtBetween( + createdAtBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - detailsIsNull() { + detailsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'details'), + ); }); } QueryBuilder - detailsIsNotNull() { + detailsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'details'), + ); }); } QueryBuilder - detailsEqualTo( - String? value, { - bool caseSensitive = true, - }) { + detailsEqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsGreaterThan( + detailsGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsLessThan( + detailsLessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsBetween( + detailsBetween( String? lower, String? upper, { bool includeLower = true, @@ -704,108 +718,109 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'details', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'details', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsStartsWith( - String value, { - bool caseSensitive = true, - }) { + detailsStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsEndsWith( - String value, { - bool caseSensitive = true, - }) { + detailsEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsContains(String value, {bool caseSensitive = true}) { + detailsContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsMatches(String pattern, {bool caseSensitive = true}) { + detailsMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'details', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'details', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsIsEmpty() { + detailsIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'details', value: ''), + ); }); } QueryBuilder - detailsIsNotEmpty() { + detailsIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'details', value: ''), + ); }); } QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -814,11 +829,13 @@ extension LoggerMessageQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -829,120 +846,124 @@ extension LoggerMessageQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - levelEqualTo(LogLevel value) { + levelEqualTo(LogLevel value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'level', value: value), + ); }); } QueryBuilder - levelGreaterThan( - LogLevel value, { - bool include = false, - }) { + levelGreaterThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelLessThan( - LogLevel value, { - bool include = false, - }) { + levelLessThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelBetween( + levelBetween( LogLevel lower, LogLevel upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'level', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'level', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - messageEqualTo( - String value, { - bool caseSensitive = true, - }) { + messageEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageGreaterThan( + messageGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageLessThan( + messageLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageBetween( + messageBetween( String lower, String upper, { bool includeLower = true, @@ -950,84 +971,86 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'message', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'message', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageStartsWith( - String value, { - bool caseSensitive = true, - }) { + messageStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageEndsWith( - String value, { - bool caseSensitive = true, - }) { + messageEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageContains(String value, {bool caseSensitive = true}) { + messageContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageMatches(String pattern, {bool caseSensitive = true}) { + messageMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'message', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'message', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageIsEmpty() { + messageIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'message', value: ''), + ); }); } QueryBuilder - messageIsNotEmpty() { + messageIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'message', value: ''), + ); }); } } @@ -1047,7 +1070,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext1Desc() { + sortByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1060,7 +1083,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext2Desc() { + sortByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1073,7 +1096,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByCreatedAtDesc() { + sortByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1125,7 +1148,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext1Desc() { + thenByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1138,7 +1161,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext2Desc() { + thenByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1151,7 +1174,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByCreatedAtDesc() { + thenByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1208,15 +1231,17 @@ extension LoggerMessageQuerySortThenBy extension LoggerMessageQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByContext1( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext1({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context1', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByContext2( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext2({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context2', caseSensitive: caseSensitive); }); @@ -1228,8 +1253,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByDetails( - {bool caseSensitive = true}) { + QueryBuilder distinctByDetails({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'details', caseSensitive: caseSensitive); }); @@ -1241,8 +1267,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByMessage( - {bool caseSensitive = true}) { + QueryBuilder distinctByMessage({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'message', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/memory.entity.drift.dart b/mobile/lib/infrastructure/entities/memory.entity.drift.dart index cb88651ba4..f5f18695a5 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.drift.dart @@ -10,65 +10,76 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$MemoryEntityTableCreateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved, - required DateTime memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); -typedef $$MemoryEntityTableUpdateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - i0.Value ownerId, - i0.Value type, - i0.Value data, - i0.Value isSaved, - i0.Value memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); +typedef $$MemoryEntityTableCreateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved, + required DateTime memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); +typedef $$MemoryEntityTableUpdateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + i0.Value ownerId, + i0.Value type, + i0.Value data, + i0.Value isSaved, + i0.Value memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); -final class $$MemoryEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$MemoryEntityTable, i1.MemoryEntityData> { +final class $$MemoryEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData + > { $$MemoryEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').ownerId, + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -82,59 +93,85 @@ class $$MemoryEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnFilters(column)); + column: $table.data, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnFilters(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.showAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -149,60 +186,84 @@ class $$MemoryEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnOrderings(column)); + column: $table.data, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.showAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -251,42 +312,52 @@ class $$MemoryEntityTableAnnotationComposer i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$MemoryEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$MemoryEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -295,74 +366,77 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< i1.$$MemoryEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value data = const i0.Value.absent(), - i0.Value isSaved = const i0.Value.absent(), - i0.Value memoryAt = const i0.Value.absent(), - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved = const i0.Value.absent(), - required DateTime memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value data = const i0.Value.absent(), + i0.Value isSaved = const i0.Value.absent(), + i0.Value memoryAt = const i0.Value.absent(), + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved = const i0.Value.absent(), + required DateTime memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -373,40 +447,50 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$MemoryEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$MemoryEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$MemoryEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $MemoryEntityTable extends i3.MemoryEntity with i0.TableInfo<$MemoryEntityTable, i1.MemoryEntityData> { @@ -417,100 +501,159 @@ class $MemoryEntityTable extends i3.MemoryEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$MemoryEntityTable.$convertertype); - static const i0.VerificationMeta _dataMeta = - const i0.VerificationMeta('data'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$MemoryEntityTable.$convertertype); + static const i0.VerificationMeta _dataMeta = const i0.VerificationMeta( + 'data', + ); @override late final i0.GeneratedColumn data = i0.GeneratedColumn( - 'data', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isSavedMeta = - const i0.VerificationMeta('isSaved'); + 'data', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isSavedMeta = const i0.VerificationMeta( + 'isSaved', + ); @override late final i0.GeneratedColumn isSaved = i0.GeneratedColumn( - 'is_saved', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _memoryAtMeta = - const i0.VerificationMeta('memoryAt'); + 'is_saved', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _memoryAtMeta = const i0.VerificationMeta( + 'memoryAt', + ); @override late final i0.GeneratedColumn memoryAt = - i0.GeneratedColumn('memory_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: true); - static const i0.VerificationMeta _seenAtMeta = - const i0.VerificationMeta('seenAt'); + i0.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _seenAtMeta = const i0.VerificationMeta( + 'seenAt', + ); @override late final i0.GeneratedColumn seenAt = i0.GeneratedColumn( - 'seen_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _showAtMeta = - const i0.VerificationMeta('showAt'); + 'seen_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _showAtMeta = const i0.VerificationMeta( + 'showAt', + ); @override late final i0.GeneratedColumn showAt = i0.GeneratedColumn( - 'show_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _hideAtMeta = - const i0.VerificationMeta('hideAt'); + 'show_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _hideAtMeta = const i0.VerificationMeta( + 'hideAt', + ); @override late final i0.GeneratedColumn hideAt = i0.GeneratedColumn( - 'hide_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -518,8 +661,9 @@ class $MemoryEntityTable extends i3.MemoryEntity static const String $name = 'memory_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -528,50 +672,70 @@ class $MemoryEntityTable extends i3.MemoryEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('data')) { context.handle( - _dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('is_saved')) { - context.handle(_isSavedMeta, - isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta)); + context.handle( + _isSavedMeta, + isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta), + ); } if (data.containsKey('memory_at')) { - context.handle(_memoryAtMeta, - memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta)); + context.handle( + _memoryAtMeta, + memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta), + ); } else if (isInserting) { context.missing(_memoryAtMeta); } if (data.containsKey('seen_at')) { - context.handle(_seenAtMeta, - seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta)); + context.handle( + _seenAtMeta, + seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta), + ); } if (data.containsKey('show_at')) { - context.handle(_showAtMeta, - showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta)); + context.handle( + _showAtMeta, + showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta), + ); } if (data.containsKey('hide_at')) { - context.handle(_hideAtMeta, - hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta)); + context.handle( + _hideAtMeta, + hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta), + ); } return context; } @@ -582,31 +746,56 @@ class $MemoryEntityTable extends i3.MemoryEntity i1.MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: i1.$MemoryEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), - data: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: i1.$MemoryEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), + data: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -637,19 +826,20 @@ class MemoryEntityData extends i0.DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -661,8 +851,9 @@ class MemoryEntityData extends i0.DataClass } map['owner_id'] = i0.Variable(ownerId); { - map['type'] = - i0.Variable(i1.$MemoryEntityTable.$convertertype.toSql(type)); + map['type'] = i0.Variable( + i1.$MemoryEntityTable.$convertertype.toSql(type), + ); } map['data'] = i0.Variable(data); map['is_saved'] = i0.Variable(isSaved); @@ -679,8 +870,10 @@ class MemoryEntityData extends i0.DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -688,8 +881,9 @@ class MemoryEntityData extends i0.DataClass updatedAt: serializer.fromJson(json['updatedAt']), deletedAt: serializer.fromJson(json['deletedAt']), ownerId: serializer.fromJson(json['ownerId']), - type: i1.$MemoryEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$MemoryEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), data: serializer.fromJson(json['data']), isSaved: serializer.fromJson(json['isSaved']), memoryAt: serializer.fromJson(json['memoryAt']), @@ -707,8 +901,9 @@ class MemoryEntityData extends i0.DataClass 'updatedAt': serializer.toJson(updatedAt), 'deletedAt': serializer.toJson(deletedAt), 'ownerId': serializer.toJson(ownerId), - 'type': serializer - .toJson(i1.$MemoryEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$MemoryEntityTable.$convertertype.toJson(type), + ), 'data': serializer.toJson(data), 'isSaved': serializer.toJson(isSaved), 'memoryAt': serializer.toJson(memoryAt), @@ -718,33 +913,33 @@ class MemoryEntityData extends i0.DataClass }; } - i1.MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value deletedAt = const i0.Value.absent(), - String? ownerId, - i2.MemoryTypeEnum? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent()}) => - i1.MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + i1.MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value deletedAt = const i0.Value.absent(), + String? ownerId, + i2.MemoryTypeEnum? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(i1.MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -782,8 +977,20 @@ class MemoryEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -842,11 +1049,11 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { this.seenAt = const i0.Value.absent(), this.showAt = const i0.Value.absent(), this.hideAt = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - type = i0.Value(type), - data = i0.Value(data), - memoryAt = i0.Value(memoryAt); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + type = i0.Value(type), + data = i0.Value(data), + memoryAt = i0.Value(memoryAt); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -877,19 +1084,20 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { }); } - i1.MemoryEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? deletedAt, - i0.Value? ownerId, - i0.Value? type, - i0.Value? data, - i0.Value? isSaved, - i0.Value? memoryAt, - i0.Value? seenAt, - i0.Value? showAt, - i0.Value? hideAt}) { + i1.MemoryEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? deletedAt, + i0.Value? ownerId, + i0.Value? type, + i0.Value? data, + i0.Value? isSaved, + i0.Value? memoryAt, + i0.Value? seenAt, + i0.Value? showAt, + i0.Value? hideAt, + }) { return i1.MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -926,7 +1134,8 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { } if (type.present) { map['type'] = i0.Variable( - i1.$MemoryEntityTable.$convertertype.toSql(type.value)); + i1.$MemoryEntityTable.$convertertype.toSql(type.value), + ); } if (data.present) { map['data'] = i0.Variable(data.value); diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart index 9253e8bc05..eedeb85e5c 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart @@ -11,74 +11,92 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i5; -typedef $$MemoryAssetEntityTableCreateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - required String assetId, - required String memoryId, -}); -typedef $$MemoryAssetEntityTableUpdateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value memoryId, -}); +typedef $$MemoryAssetEntityTableCreateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + required String assetId, + required String memoryId, + }); +typedef $$MemoryAssetEntityTableUpdateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value memoryId, + }); -final class $$MemoryAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData> { +final class $$MemoryAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData + > { $$MemoryAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('memory_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .memoryId, - i4.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').id, + ), + ); i5.$$MemoryEntityTableProcessedTableManager get memoryId { final $_column = $_itemColumn('memory_id')!; final manager = i5 .$$MemoryEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('memory_entity')) + ).resultSet('memory_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_memoryIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -93,45 +111,55 @@ class $$MemoryAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableFilterComposer get memoryId { final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,46 +176,55 @@ class $$MemoryAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableOrderingComposer get memoryId { final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -204,65 +241,79 @@ class $$MemoryAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableAnnotationComposer get memoryId { final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})> { +class $$MemoryAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + > { $$MemoryAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -271,35 +322,38 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< .$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value memoryId = const i0.Value.absent(), - }) => - i1.MemoryAssetEntityCompanion( - assetId: assetId, - memoryId: memoryId, - ), - createCompanionCallback: ({ - required String assetId, - required String memoryId, - }) => - i1.MemoryAssetEntityCompanion.insert( - assetId: assetId, - memoryId: memoryId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value memoryId = const i0.Value.absent(), + }) => i1.MemoryAssetEntityCompanion( + assetId: assetId, + memoryId: memoryId, + ), + createCompanionCallback: + ({required String assetId, required String memoryId}) => + i1.MemoryAssetEntityCompanion.insert( + assetId: assetId, + memoryId: memoryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, memoryId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -310,53 +364,65 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$MemoryAssetEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (memoryId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.memoryId, - referencedTable: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (memoryId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.memoryId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})>; +typedef $$MemoryAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + >; class $MemoryAssetEntityTable extends i2.MemoryAssetEntity with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> { @@ -364,24 +430,34 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $MemoryAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _memoryIdMeta = - const i0.VerificationMeta('memoryId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _memoryIdMeta = const i0.VerificationMeta( + 'memoryId', + ); @override late final i0.GeneratedColumn memoryId = i0.GeneratedColumn( - 'memory_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -391,19 +467,24 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity static const String $name = 'memory_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('memory_id')) { - context.handle(_memoryIdMeta, - memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta)); + context.handle( + _memoryIdMeta, + memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta), + ); } else if (isInserting) { context.missing(_memoryIdMeta); } @@ -413,14 +494,20 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity @override Set get $primaryKey => {assetId, memoryId}; @override - i1.MemoryAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.MemoryAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -448,8 +535,10 @@ class MemoryAssetEntityData extends i0.DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -507,8 +596,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = i0.Value(assetId), - memoryId = i0.Value(memoryId); + }) : assetId = i0.Value(assetId), + memoryId = i0.Value(memoryId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? memoryId, @@ -519,8 +608,10 @@ class MemoryAssetEntityCompanion }); } - i1.MemoryAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? memoryId}) { + i1.MemoryAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? memoryId, + }) { return i1.MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 7f56b25d4e..75f8de2de0 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -16,87 +16,100 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset(List var1, - {required MergedAsset$limit limit}) { + i0.Selectable mergedAsset( + List var1, { + required MergedAsset$limit limit, + }) { var $arrayStartIndex = 1; final expandedvar1 = $expandVar($arrayStartIndex, var1.length); $arrayStartIndex += var1.length; - final generatedlimit = $write(limit(alias(this.localAssetEntity, 'lae')), - startIndex: $arrayStartIndex); + final generatedlimit = $write( + limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex, + ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', - variables: [ - for (var $ in var1) i0.Variable($), - ...generatedlimit.introducedVariables - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - localAlbumAssetEntity, - localAlbumEntity, - ...generatedlimit.watchedTables, - }).map((i0.QueryRow row) => MergedAssetResult( - remoteId: row.readNullable('remote_id'), - localId: row.readNullable('local_id'), - name: row.read('name'), - type: i4.$RemoteAssetEntityTable.$convertertype - .fromSql(row.read('type')), - createdAt: row.read('created_at'), - updatedAt: row.read('updated_at'), - width: row.readNullable('width'), - height: row.readNullable('height'), - durationInSeconds: row.readNullable('duration_in_seconds'), - isFavorite: row.read('is_favorite'), - thumbHash: row.readNullable('thumb_hash'), - checksum: row.readNullable('checksum'), - ownerId: row.readNullable('owner_id'), - livePhotoVideoId: row.readNullable('live_photo_video_id'), - orientation: row.read('orientation'), - stackId: row.readNullable('stack_id'), - )); + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + variables: [ + for (var $ in var1) i0.Variable($), + ...generatedlimit.introducedVariables, + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + stackEntity, + localAlbumAssetEntity, + localAlbumEntity, + ...generatedlimit.watchedTables, + }, + ).map( + (i0.QueryRow row) => MergedAssetResult( + remoteId: row.readNullable('remote_id'), + localId: row.readNullable('local_id'), + name: row.read('name'), + type: i4.$RemoteAssetEntityTable.$convertertype.fromSql( + row.read('type'), + ), + createdAt: row.read('created_at'), + updatedAt: row.read('updated_at'), + width: row.readNullable('width'), + height: row.readNullable('height'), + durationInSeconds: row.readNullable('duration_in_seconds'), + isFavorite: row.read('is_favorite'), + thumbHash: row.readNullable('thumb_hash'), + checksum: row.readNullable('checksum'), + ownerId: row.readNullable('owner_id'), + livePhotoVideoId: row.readNullable('live_photo_video_id'), + orientation: row.read('orientation'), + stackId: row.readNullable('stack_id'), + ), + ); } - i0.Selectable mergedBucket(List var2, - {required int groupBy}) { + i0.Selectable mergedBucket( + List var2, { + required int groupBy, + }) { var $arrayStartIndex = 2; final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', - variables: [ - i0.Variable(groupBy), - for (var $ in var2) i0.Variable($) - ], - readsFrom: { - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumAssetEntity, - localAlbumEntity, - }).map((i0.QueryRow row) => MergedBucketResult( - assetCount: row.read('asset_count'), - bucketDate: row.read('bucket_date'), - )); + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + variables: [ + i0.Variable(groupBy), + for (var $ in var2) i0.Variable($), + ], + readsFrom: { + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, + }, + ).map( + (i0.QueryRow row) => MergedBucketResult( + assetCount: row.read('asset_count'), + bucketDate: row.read('bucket_date'), + ), + ); } - i4.$RemoteAssetEntityTable get remoteAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i5.$StackEntityTable get stackEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('stack_entity'); - i3.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('remote_asset_entity'); + i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_asset_entity'); i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet( - 'local_album_asset_entity'); - i7.$LocalAlbumEntityTable get localAlbumEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_album_entity'); + i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_entity'); } class MergedAssetResult { @@ -141,8 +154,5 @@ typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); class MergedBucketResult { final int assetCount; final String bucketDate; - MergedBucketResult({ - required this.assetCount, - required this.bucketDate, - }); + MergedBucketResult({required this.assetCount, required this.bucketDate}); } diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart index 26a5dd2fe0..01ec72fe23 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.drift.dart @@ -10,74 +10,94 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline, -}); -typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - i0.Value sharedById, - i0.Value sharedWithId, - i0.Value inTimeline, -}); +typedef $$PartnerEntityTableCreateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline, + }); +typedef $$PartnerEntityTableUpdateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + i0.Value sharedById, + i0.Value sharedWithId, + i0.Value inTimeline, + }); -final class $$PartnerEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PartnerEntityTable, i1.PartnerEntityData> { +final class $$PartnerEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData + > { $$PartnerEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('partner_entity') - .sharedById, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('partner_entity').sharedById, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedById { final $_column = $_itemColumn('shared_by_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet('partner_entity') .sharedWithId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedWithId { final $_column = $_itemColumn('shared_with_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -91,49 +111,61 @@ class $$PartnerEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get sharedById { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableFilterComposer get sharedWithId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,50 +180,61 @@ class $$PartnerEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get inTimeline => $composableBuilder( - column: $table.inTimeline, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get sharedById { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableOrderingComposer get sharedWithId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -206,68 +249,85 @@ class $$PartnerEntityTableAnnotationComposer super.$removeJoinBuilderFromRootComposer, }); i0.GeneratedColumn get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => column); + column: $table.inTimeline, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get sharedById { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableAnnotationComposer get sharedWithId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PartnerEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})> { +class $$PartnerEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + > { $$PartnerEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PartnerEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PartnerEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -276,38 +336,41 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< i1.$$PartnerEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value sharedById = const i0.Value.absent(), - i0.Value sharedWithId = const i0.Value.absent(), - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), - createCompanionCallback: ({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion.insert( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), + updateCompanionCallback: + ({ + i0.Value sharedById = const i0.Value.absent(), + i0.Value sharedWithId = const i0.Value.absent(), + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + createCompanionCallback: + ({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion.insert( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PartnerEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PartnerEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({sharedById = false, sharedWithId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,52 +381,65 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (sharedById) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedById, - referencedTable: - i1.$$PartnerEntityTableReferences._sharedByIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedByIdTable(db) - .id, - ) as T; - } - if (sharedWithId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedWithId, - referencedTable: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (sharedById) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedById, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db) + .id, + ) + as T; + } + if (sharedWithId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedWithId, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>; +typedef $$PartnerEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + >; class $PartnerEntityTable extends i2.PartnerEntity with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> { @@ -371,37 +447,55 @@ class $PartnerEntityTable extends i2.PartnerEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $PartnerEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _sharedByIdMeta = - const i0.VerificationMeta('sharedById'); + static const i0.VerificationMeta _sharedByIdMeta = const i0.VerificationMeta( + 'sharedById', + ); @override late final i0.GeneratedColumn sharedById = i0.GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _sharedWithIdMeta = const i0.VerificationMeta('sharedWithId'); @override late final i0.GeneratedColumn sharedWithId = - i0.GeneratedColumn('shared_with_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _inTimelineMeta = - const i0.VerificationMeta('inTimeline'); + i0.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _inTimelineMeta = const i0.VerificationMeta( + 'inTimeline', + ); @override late final i0.GeneratedColumn inTimeline = i0.GeneratedColumn( - 'in_timeline', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const i3.Constant(false)); + 'in_timeline', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); @override - List get $columns => - [sharedById, sharedWithId, inTimeline]; + List get $columns => [ + sharedById, + sharedWithId, + inTimeline, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -409,31 +503,38 @@ class $PartnerEntityTable extends i2.PartnerEntity static const String $name = 'partner_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('shared_by_id')) { context.handle( + _sharedByIdMeta, + sharedById.isAcceptableOrUnknown( + data['shared_by_id']!, _sharedByIdMeta, - sharedById.isAcceptableOrUnknown( - data['shared_by_id']!, _sharedByIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedByIdMeta); } if (data.containsKey('shared_with_id')) { context.handle( + _sharedWithIdMeta, + sharedWithId.isAcceptableOrUnknown( + data['shared_with_id']!, _sharedWithIdMeta, - sharedWithId.isAcceptableOrUnknown( - data['shared_with_id']!, _sharedWithIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedWithIdMeta); } if (data.containsKey('in_timeline')) { context.handle( - _inTimelineMeta, - inTimeline.isAcceptableOrUnknown( - data['in_timeline']!, _inTimelineMeta)); + _inTimelineMeta, + inTimeline.isAcceptableOrUnknown(data['in_timeline']!, _inTimelineMeta), + ); } return context; } @@ -445,11 +546,17 @@ class $PartnerEntityTable extends i2.PartnerEntity final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PartnerEntityData( sharedById: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, sharedWithId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -469,10 +576,11 @@ class PartnerEntityData extends i0.DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -482,8 +590,10 @@ class PartnerEntityData extends i0.DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -501,22 +611,26 @@ class PartnerEntityData extends i0.DataClass }; } - i1.PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - i1.PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + i1.PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => i1.PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(i1.PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -554,8 +668,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const i0.Value.absent(), - }) : sharedById = i0.Value(sharedById), - sharedWithId = i0.Value(sharedWithId); + }) : sharedById = i0.Value(sharedById), + sharedWithId = i0.Value(sharedWithId); static i0.Insertable custom({ i0.Expression? sharedById, i0.Expression? sharedWithId, @@ -568,10 +682,11 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { }); } - i1.PartnerEntityCompanion copyWith( - {i0.Value? sharedById, - i0.Value? sharedWithId, - i0.Value? inTimeline}) { + i1.PartnerEntityCompanion copyWith({ + i0.Value? sharedById, + i0.Value? sharedWithId, + i0.Value? inTimeline, + }) { return i1.PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart index 70639adc2f..ffbd796f4b 100644 --- a/mobile/lib/infrastructure/entities/person.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -9,61 +9,72 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String name, - i0.Value faceAssetId, - required bool isFavorite, - required bool isHidden, - i0.Value color, - i0.Value birthDate, -}); -typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value name, - i0.Value faceAssetId, - i0.Value isFavorite, - i0.Value isHidden, - i0.Value color, - i0.Value birthDate, -}); +typedef $$PersonEntityTableCreateCompanionBuilder = + i1.PersonEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String name, + i0.Value faceAssetId, + required bool isFavorite, + required bool isHidden, + i0.Value color, + i0.Value birthDate, + }); +typedef $$PersonEntityTableUpdateCompanionBuilder = + i1.PersonEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value name, + i0.Value faceAssetId, + i0.Value isFavorite, + i0.Value isHidden, + i0.Value color, + i0.Value birthDate, + }); -final class $$PersonEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PersonEntityTable, i1.PersonEntityData> { +final class $$PersonEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData + > { $$PersonEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('person_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('person_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -77,52 +88,74 @@ class $$PersonEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnFilters(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnFilters(column)); + column: $table.color, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get birthDate => $composableBuilder( - column: $table.birthDate, builder: (column) => i0.ColumnFilters(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -137,56 +170,74 @@ class $$PersonEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnOrderings(column)); + column: $table.color, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get birthDate => $composableBuilder( - column: $table.birthDate, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -213,10 +264,14 @@ class $$PersonEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get faceAssetId => $composableBuilder( - column: $table.faceAssetId, builder: (column) => column); + column: $table.faceAssetId, + builder: (column) => column, + ); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get isHidden => $composableBuilder(column: $table.isHidden, builder: (column) => column); @@ -229,42 +284,52 @@ class $$PersonEntityTableAnnotationComposer i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PersonEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$PersonEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$PersonEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PersonEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PersonEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -273,66 +338,69 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< i1.$$PersonEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PersonEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value faceAssetId = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value isHidden = const i0.Value.absent(), - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String name, - i0.Value faceAssetId = const i0.Value.absent(), - required bool isFavorite, - required bool isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value faceAssetId = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value isHidden = const i0.Value.absent(), + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + i0.Value faceAssetId = const i0.Value.absent(), + required bool isFavorite, + required bool isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PersonEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PersonEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -343,40 +411,50 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$PersonEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$PersonEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PersonEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$PersonEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $PersonEntityTable extends i2.PersonEntity with i0.TableInfo<$PersonEntityTable, i1.PersonEntityData> { @@ -387,88 +465,139 @@ class $PersonEntityTable extends i2.PersonEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _faceAssetIdMeta = - const i0.VerificationMeta('faceAssetId'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _faceAssetIdMeta = const i0.VerificationMeta( + 'faceAssetId', + ); @override late final i0.GeneratedColumn faceAssetId = - i0.GeneratedColumn('face_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + i0.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); - static const i0.VerificationMeta _isHiddenMeta = - const i0.VerificationMeta('isHidden'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _isHiddenMeta = const i0.VerificationMeta( + 'isHidden', + ); @override late final i0.GeneratedColumn isHidden = i0.GeneratedColumn( - 'is_hidden', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_hidden" IN (0, 1))')); - static const i0.VerificationMeta _colorMeta = - const i0.VerificationMeta('color'); + 'is_hidden', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _colorMeta = const i0.VerificationMeta( + 'color', + ); @override late final i0.GeneratedColumn color = i0.GeneratedColumn( - 'color', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _birthDateMeta = - const i0.VerificationMeta('birthDate'); + 'color', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _birthDateMeta = const i0.VerificationMeta( + 'birthDate', + ); @override late final i0.GeneratedColumn birthDate = - i0.GeneratedColumn('birth_date', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -476,8 +605,9 @@ class $PersonEntityTable extends i2.PersonEntity static const String $name = 'person_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -486,52 +616,69 @@ class $PersonEntityTable extends i2.PersonEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('face_asset_id')) { context.handle( + _faceAssetIdMeta, + faceAssetId.isAcceptableOrUnknown( + data['face_asset_id']!, _faceAssetIdMeta, - faceAssetId.isAcceptableOrUnknown( - data['face_asset_id']!, _faceAssetIdMeta)); + ), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } else if (isInserting) { context.missing(_isFavoriteMeta); } if (data.containsKey('is_hidden')) { - context.handle(_isHiddenMeta, - isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta)); + context.handle( + _isHiddenMeta, + isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta), + ); } else if (isInserting) { context.missing(_isHiddenMeta); } if (data.containsKey('color')) { context.handle( - _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + _colorMeta, + color.isAcceptableOrUnknown(data['color']!, _colorMeta), + ); } if (data.containsKey('birth_date')) { - context.handle(_birthDateMeta, - birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta)); + context.handle( + _birthDateMeta, + birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta), + ); } return context; } @@ -542,26 +689,46 @@ class $PersonEntityTable extends i2.PersonEntity i1.PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PersonEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, faceAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + i0.DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -588,17 +755,18 @@ class PersonEntityData extends i0.DataClass final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -621,8 +789,10 @@ class PersonEntityData extends i0.DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -654,29 +824,29 @@ class PersonEntityData extends i0.DataClass }; } - i1.PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - i0.Value faceAssetId = const i0.Value.absent(), - bool? isFavorite, - bool? isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent()}) => - i1.PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + i1.PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + i0.Value faceAssetId = const i0.Value.absent(), + bool? isFavorite, + bool? isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(i1.PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -684,10 +854,12 @@ class PersonEntityData extends i0.DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -712,8 +884,18 @@ class PersonEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -764,11 +946,11 @@ class PersonEntityCompanion extends i0.UpdateCompanion { required bool isHidden, this.color = const i0.Value.absent(), this.birthDate = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - name = i0.Value(name), - isFavorite = i0.Value(isFavorite), - isHidden = i0.Value(isHidden); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + name = i0.Value(name), + isFavorite = i0.Value(isFavorite), + isHidden = i0.Value(isHidden); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -795,17 +977,18 @@ class PersonEntityCompanion extends i0.UpdateCompanion { }); } - i1.PersonEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? name, - i0.Value? faceAssetId, - i0.Value? isFavorite, - i0.Value? isHidden, - i0.Value? color, - i0.Value? birthDate}) { + i1.PersonEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? name, + i0.Value? faceAssetId, + i0.Value? isFavorite, + i0.Value? isHidden, + i0.Value? color, + i0.Value? birthDate, + }) { return i1.PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart index bc13c8cb5c..30a6d0b535 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart @@ -13,89 +13,107 @@ import 'package:drift/internal/modular.dart' as i6; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i7; -typedef $$RemoteAlbumEntityTableCreateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - required i2.AlbumAssetOrder order, -}); -typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - i0.Value order, -}); +typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + required i2.AlbumAssetOrder order, + }); +typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + i0.Value order, + }); -final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData> { +final class $$RemoteAlbumEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData + > { $$RemoteAlbumEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_album_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable( - i0.GeneratedDatabase db) => - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .thumbnailAssetId, - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i0.GeneratedDatabase db, + ) => i6.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('remote_album_entity') + .thumbnailAssetId, + i6.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId { final $_column = $_itemColumn('thumbnail_asset_id'); if ($_column == null) return null; final manager = i7 .$$RemoteAssetEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -109,71 +127,92 @@ class $$RemoteAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get order => $composableBuilder( - column: $table.order, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get order => $composableBuilder( + column: $table.order, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -188,73 +227,92 @@ class $$RemoteAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get order => $composableBuilder( - column: $table.order, builder: (column) => i0.ColumnOrderings(column)); + column: $table.order, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -275,7 +333,9 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -284,73 +344,89 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, builder: (column) => column); + column: $table.isActivityEnabled, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get order => $composableBuilder(column: $table.order, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> { +class $$RemoteAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + > { $$RemoteAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -359,63 +435,68 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< .$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - i0.Value order = const i0.Value.absent(), - }) => - i1.RemoteAlbumEntityCompanion( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - required i2.AlbumAssetOrder order, - }) => - i1.RemoteAlbumEntityCompanion.insert( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + i0.Value order = const i0.Value.absent(), + }) => i1.RemoteAlbumEntityCompanion( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + required i2.AlbumAssetOrder order, + }) => i1.RemoteAlbumEntityCompanion.insert( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -426,53 +507,65 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } - if (thumbnailAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.thumbnailAssetId, - referencedTable: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } + if (thumbnailAssetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.thumbnailAssetId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>; +typedef $$RemoteAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + >; class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> { @@ -483,84 +576,129 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const i4.Constant('')); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'description', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const i4.Constant(''), + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _thumbnailAssetIdMeta = const i0.VerificationMeta('thumbnailAssetId'); @override late final i0.GeneratedColumn thumbnailAssetId = - i0.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i0.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); static const i0.VerificationMeta _isActivityEnabledMeta = const i0.VerificationMeta('isActivityEnabled'); @override late final i0.GeneratedColumn isActivityEnabled = - i0.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const i4.Constant(true)); + i0.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const i4.Constant(true), + ); @override late final i0.GeneratedColumnWithTypeConverter - order = i0.GeneratedColumn('order', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumEntityTable.$converterorder); + order = + i0.GeneratedColumn( + 'order', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumEntityTable.$converterorder, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -568,8 +706,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const String $name = 'remote_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -579,41 +718,58 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('thumbnail_asset_id')) { context.handle( + _thumbnailAssetIdMeta, + thumbnailAssetId.isAcceptableOrUnknown( + data['thumbnail_asset_id']!, _thumbnailAssetIdMeta, - thumbnailAssetId.isAcceptableOrUnknown( - data['thumbnail_asset_id']!, _thumbnailAssetIdMeta)); + ), + ); } if (data.containsKey('is_activity_enabled')) { context.handle( + _isActivityEnabledMeta, + isActivityEnabled.isAcceptableOrUnknown( + data['is_activity_enabled']!, _isActivityEnabledMeta, - isActivityEnabled.isAcceptableOrUnknown( - data['is_activity_enabled']!, _isActivityEnabledMeta)); + ), + ); } return context; } @@ -621,29 +777,50 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ), ); } @@ -654,7 +831,8 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static i0.JsonTypeConverter2 $converterorder = const i0.EnumIndexConverter( - i2.AlbumAssetOrder.values); + i2.AlbumAssetOrder.values, + ); @override bool get withoutRowId => true; @override @@ -672,16 +850,17 @@ class RemoteAlbumEntityData extends i0.DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final i2.AlbumAssetOrder order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -697,13 +876,16 @@ class RemoteAlbumEntityData extends i0.DataClass map['is_activity_enabled'] = i0.Variable(isActivityEnabled); { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order), + ); } return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -714,8 +896,9 @@ class RemoteAlbumEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), - order: i1.$RemoteAlbumEntityTable.$converterorder - .fromJson(serializer.fromJson(json['order'])), + order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson( + serializer.fromJson(json['order']), + ), ); } @override @@ -731,39 +914,41 @@ class RemoteAlbumEntityData extends i0.DataClass 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), 'isActivityEnabled': serializer.toJson(isActivityEnabled), 'order': serializer.toJson( - i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)), + i1.$RemoteAlbumEntityTable.$converterorder.toJson(order), + ), }; } - i1.RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - bool? isActivityEnabled, - i2.AlbumAssetOrder? order}) => - i1.RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + i1.RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + bool? isActivityEnabled, + i2.AlbumAssetOrder? order, + }) => i1.RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -794,8 +979,17 @@ class RemoteAlbumEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -843,10 +1037,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(), required i2.AlbumAssetOrder order, - }) : id = i0.Value(id), - name = i0.Value(name), - ownerId = i0.Value(ownerId), - order = i0.Value(order); + }) : id = i0.Value(id), + name = i0.Value(name), + ownerId = i0.Value(ownerId), + order = i0.Value(order); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -871,16 +1065,17 @@ class RemoteAlbumEntityCompanion }); } - i1.RemoteAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? description, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? thumbnailAssetId, - i0.Value? isActivityEnabled, - i0.Value? order}) { + i1.RemoteAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? description, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? thumbnailAssetId, + i0.Value? isActivityEnabled, + i0.Value? order, + }) { return i1.RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -923,7 +1118,8 @@ class RemoteAlbumEntityCompanion } if (order.present) { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart index ab50607c96..adf22635c1 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' as i5; -typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { +final class $$RemoteAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { $$RemoteAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$RemoteAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$RemoteAlbumAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableFilterComposer get albumId { final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$RemoteAlbumAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i5.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$RemoteAlbumAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i5.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$RemoteAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$RemoteAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.RemoteAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.RemoteAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.RemoteAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.RemoteAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,107 @@ class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, +typedef $$RemoteAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableReferences - ), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity with - i0.TableInfo<$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { + i0.TableInfo< + $RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +489,24 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity static const String $name = 'remote_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +516,20 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +548,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +560,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +585,8 @@ class RemoteAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - i1.RemoteAlbumAssetEntityCompanion data) { + i1.RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +623,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +635,10 @@ class RemoteAlbumAssetEntityCompanion }); } - i1.RemoteAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.RemoteAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart index 7ec1151a8a..f8167ddf94 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart @@ -12,78 +12,98 @@ import 'package:drift/internal/modular.dart' as i5; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i6; -typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, -}); -typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - i0.Value albumId, - i0.Value userId, - i0.Value role, -}); +typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }); +typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + i0.Value albumId, + i0.Value userId, + i0.Value role, + }); -final class $$RemoteAlbumUserEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData> { +final class $$RemoteAlbumUserEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { $$RemoteAlbumUserEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .albumId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i4 .$$RemoteAlbumEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .userId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i6.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i6 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -97,51 +117,62 @@ class $$RemoteAlbumUserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get role => $composableBuilder( - column: $table.role, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get role => $composableBuilder( + column: $table.role, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i4.$$RemoteAlbumEntityTableFilterComposer get albumId { final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableFilterComposer get userId { final i6.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -156,51 +187,62 @@ class $$RemoteAlbumUserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get role => $composableBuilder( - column: $table.role, builder: (column) => i0.ColumnOrderings(column)); + column: $table.role, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i4.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableOrderingComposer get userId { final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -220,108 +262,134 @@ class $$RemoteAlbumUserEntityTableAnnotationComposer i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i4.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableAnnotationComposer get userId { final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})> { +class $$RemoteAlbumUserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableReferences, + ), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + > { $$RemoteAlbumUserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumUserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumUserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumUserEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumUserEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumUserEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value albumId = const i0.Value.absent(), - i0.Value userId = const i0.Value.absent(), - i0.Value role = const i0.Value.absent(), - }) => - i1.RemoteAlbumUserEntityCompanion( - albumId: albumId, - userId: userId, - role: role, - ), - createCompanionCallback: ({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, - }) => - i1.RemoteAlbumUserEntityCompanion.insert( - albumId: albumId, - userId: userId, - role: role, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value albumId = const i0.Value.absent(), + i0.Value userId = const i0.Value.absent(), + i0.Value role = const i0.Value.absent(), + }) => i1.RemoteAlbumUserEntityCompanion( + albumId: albumId, + userId: userId, + role: role, + ), + createCompanionCallback: + ({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }) => i1.RemoteAlbumUserEntityCompanion.insert( + albumId: albumId, + userId: userId, + role: role, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumUserEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumUserEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({albumId = false, userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -332,89 +400,115 @@ class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumUserEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableReferences - ), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})>; +typedef $$RemoteAlbumUserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + >; class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity with - i0 - .TableInfo<$RemoteAlbumUserEntityTable, i1.RemoteAlbumUserEntityData> { + i0.TableInfo< + $RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter role = - i0.GeneratedColumn('role', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumUserEntityTable.$converterrole); + i0.GeneratedColumn( + 'role', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumUserEntityTable.$converterrole, + ); @override List get $columns => [albumId, userId, role]; @override @@ -424,19 +518,24 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity static const String $name = 'remote_album_user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -446,17 +545,26 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity @override Set get $primaryKey => {albumId, userId}; @override - i1.RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ), ); } @@ -478,8 +586,11 @@ class RemoteAlbumUserEntityData extends i0.DataClass final String albumId; final String userId; final i2.AlbumUserRole role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -487,19 +598,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass map['user_id'] = i0.Variable(userId); { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role), + ); } return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), userId: serializer.fromJson(json['userId']), - role: i1.$RemoteAlbumUserEntityTable.$converterrole - .fromJson(serializer.fromJson(json['role'])), + role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromJson( + serializer.fromJson(json['role']), + ), ); } @override @@ -509,19 +624,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass 'albumId': serializer.toJson(albumId), 'userId': serializer.toJson(userId), 'role': serializer.toJson( - i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role)), + i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role), + ), }; } - i1.RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, i2.AlbumUserRole? role}) => - i1.RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + i1.RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + i2.AlbumUserRole? role, + }) => i1.RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - i1.RemoteAlbumUserEntityCompanion data) { + i1.RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -564,9 +683,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required i2.AlbumUserRole role, - }) : albumId = i0.Value(albumId), - userId = i0.Value(userId), - role = i0.Value(role); + }) : albumId = i0.Value(albumId), + userId = i0.Value(userId), + role = i0.Value(role); static i0.Insertable custom({ i0.Expression? albumId, i0.Expression? userId, @@ -579,10 +698,11 @@ class RemoteAlbumUserEntityCompanion }); } - i1.RemoteAlbumUserEntityCompanion copyWith( - {i0.Value? albumId, - i0.Value? userId, - i0.Value? role}) { + i1.RemoteAlbumUserEntityCompanion copyWith({ + i0.Value? albumId, + i0.Value? userId, + i0.Value? role, + }) { return i1.RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -601,7 +721,8 @@ class RemoteAlbumUserEntityCompanion } if (role.present) { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index cb14e2501d..7ee2e76ff6 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex( - name: 'UQ_remote_asset_owner_checksum', - columns: {#checksum, #ownerId}, - unique: true, -) +@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true) @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); @@ -40,21 +36,21 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { RemoteAsset toDto() => RemoteAsset( - id: id, - name: name, - ownerId: ownerId, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - thumbHash: thumbHash, - visibility: visibility, - livePhotoVideoId: livePhotoVideoId, - localId: null, - stackId: stackId, - ); + id: id, + name: name, + ownerId: ownerId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + thumbHash: thumbHash, + visibility: visibility, + livePhotoVideoId: livePhotoVideoId, + localId: null, + stackId: stackId, + ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 543ed65985..6bd416b259 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -11,78 +11,90 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$RemoteAssetEntityTableCreateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - required String checksum, - i0.Value isFavorite, - required String ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - required i2.AssetVisibility visibility, - i0.Value stackId, -}); -typedef $$RemoteAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - i0.Value visibility, - i0.Value stackId, -}); +typedef $$RemoteAssetEntityTableCreateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + required String checksum, + i0.Value isFavorite, + required String ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + required i2.AssetVisibility visibility, + i0.Value stackId, + }); +typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + i0.Value visibility, + i0.Value stackId, + }); -final class $$RemoteAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData> { +final class $$RemoteAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData + > { $$RemoteAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -96,79 +108,111 @@ class $$RemoteAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get thumbHash => $composableBuilder( - column: $table.thumbHash, builder: (column) => i0.ColumnFilters(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get visibility => $composableBuilder( + column: $table.visibility, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnFilters(column)); + column: $table.stackId, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -183,81 +227,109 @@ class $$RemoteAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get thumbHash => $composableBuilder( - column: $table.thumbHash, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.visibility, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnOrderings(column)); + column: $table.stackId, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -290,7 +362,9 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -299,10 +373,14 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get localDateTime => $composableBuilder( - column: $table.localDateTime, builder: (column) => column); + column: $table.localDateTime, + builder: (column) => column, + ); i0.GeneratedColumn get thumbHash => $composableBuilder(column: $table.thumbHash, builder: (column) => column); @@ -311,53 +389,67 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.deletedAt, builder: (column) => column); i0.GeneratedColumn get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, builder: (column) => column); + column: $table.livePhotoVideoId, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get visibility => $composableBuilder( - column: $table.visibility, builder: (column) => column); + column: $table.visibility, + builder: (column) => column, + ); i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$RemoteAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$RemoteAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -366,95 +458,101 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< .$$RemoteAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i0.Value visibility = const i0.Value.absent(), - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - required String checksum, - i0.Value isFavorite = const i0.Value.absent(), - required String ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - required i2.AssetVisibility visibility, - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i0.Value visibility = + const i0.Value.absent(), + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + required String checksum, + i0.Value isFavorite = const i0.Value.absent(), + required String ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + required i2.AssetVisibility visibility, + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -465,45 +563,54 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAssetEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAssetEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$RemoteAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', +); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity with i0.TableInfo<$RemoteAssetEntityTable, i1.RemoteAssetEntityData> { @@ -511,138 +618,222 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$RemoteAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _localDateTimeMeta = const i0.VerificationMeta('localDateTime'); @override late final i0.GeneratedColumn localDateTime = - i0.GeneratedColumn('local_date_time', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbHashMeta = - const i0.VerificationMeta('thumbHash'); + i0.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _thumbHashMeta = const i0.VerificationMeta( + 'thumbHash', + ); @override late final i0.GeneratedColumn thumbHash = i0.GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + 'thumb_hash', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _livePhotoVideoIdMeta = const i0.VerificationMeta('livePhotoVideoId'); @override late final i0.GeneratedColumn livePhotoVideoId = - i0.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override late final i0.GeneratedColumnWithTypeConverter - visibility = i0.GeneratedColumn('visibility', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertervisibility); - static const i0.VerificationMeta _stackIdMeta = - const i0.VerificationMeta('stackId'); + visibility = + i0.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAssetEntityTable.$convertervisibility, + ); + static const i0.VerificationMeta _stackIdMeta = const i0.VerificationMeta( + 'stackId', + ); @override late final i0.GeneratedColumn stackId = i0.GeneratedColumn( - 'stack_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -650,37 +841,51 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static const String $name = 'remote_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -688,46 +893,62 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } else if (isInserting) { context.missing(_checksumMeta); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('local_date_time')) { context.handle( + _localDateTimeMeta, + localDateTime.isAcceptableOrUnknown( + data['local_date_time']!, _localDateTimeMeta, - localDateTime.isAcceptableOrUnknown( - data['local_date_time']!, _localDateTimeMeta)); + ), + ); } if (data.containsKey('thumb_hash')) { - context.handle(_thumbHashMeta, - thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta)); + context.handle( + _thumbHashMeta, + thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('live_photo_video_id')) { context.handle( + _livePhotoVideoIdMeta, + livePhotoVideoId.isAcceptableOrUnknown( + data['live_photo_video_id']!, _livePhotoVideoIdMeta, - livePhotoVideoId.isAcceptableOrUnknown( - data['live_photo_video_id']!, _livePhotoVideoIdMeta)); + ), + ); } if (data.containsKey('stack_id')) { - context.handle(_stackIdMeta, - stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta)); + context.handle( + _stackIdMeta, + stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), + ); } return context; } @@ -735,47 +956,84 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$RemoteAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$RemoteAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}live_photo_video_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql( - attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!), - stackId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}stack_id']), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + ), + stackId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -787,8 +1045,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static i0.JsonTypeConverter2 $convertertype = const i0.EnumIndexConverter(i2.AssetType.values); static i0.JsonTypeConverter2 - $convertervisibility = const i0.EnumIndexConverter( - i2.AssetVisibility.values); + $convertervisibility = const i0.EnumIndexConverter( + i2.AssetVisibility.values, + ); @override bool get withoutRowId => true; @override @@ -814,31 +1073,33 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -869,7 +1130,8 @@ class RemoteAssetEntityData extends i0.DataClass } { map['visibility'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility)); + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility), + ); } if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); @@ -877,13 +1139,16 @@ class RemoteAssetEntityData extends i0.DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$RemoteAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$RemoteAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -897,8 +1162,9 @@ class RemoteAssetEntityData extends i0.DataClass thumbHash: serializer.fromJson(json['thumbHash']), deletedAt: serializer.fromJson(json['deletedAt']), livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), - visibility: i1.$RemoteAssetEntityTable.$convertervisibility - .fromJson(serializer.fromJson(json['visibility'])), + visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromJson( + serializer.fromJson(json['visibility']), + ), stackId: serializer.fromJson(json['stackId']), ); } @@ -907,8 +1173,9 @@ class RemoteAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$RemoteAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -923,53 +1190,55 @@ class RemoteAssetEntityData extends i0.DataClass 'deletedAt': serializer.toJson(deletedAt), 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), 'visibility': serializer.toJson( - i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)), + i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), + ), 'stackId': serializer.toJson(stackId), }; } - i1.RemoteAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i2.AssetVisibility? visibility, - i0.Value stackId = const i0.Value.absent()}) => - i1.RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + i1.RemoteAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i2.AssetVisibility? visibility, + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -983,8 +1252,9 @@ class RemoteAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -994,8 +1264,9 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -1026,23 +1297,24 @@ class RemoteAssetEntityData extends i0.DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1122,12 +1394,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id), - checksum = i0.Value(checksum), - ownerId = i0.Value(ownerId), - visibility = i0.Value(visibility); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id), + checksum = i0.Value(checksum), + ownerId = i0.Value(ownerId), + visibility = i0.Value(visibility); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -1168,24 +1440,25 @@ class RemoteAssetEntityCompanion }); } - i1.RemoteAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? ownerId, - i0.Value? localDateTime, - i0.Value? thumbHash, - i0.Value? deletedAt, - i0.Value? livePhotoVideoId, - i0.Value? visibility, - i0.Value? stackId}) { + i1.RemoteAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? ownerId, + i0.Value? localDateTime, + i0.Value? thumbHash, + i0.Value? deletedAt, + i0.Value? livePhotoVideoId, + i0.Value? visibility, + i0.Value? stackId, + }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1215,7 +1488,8 @@ class RemoteAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); @@ -1257,9 +1531,9 @@ class RemoteAssetEntityCompanion map['live_photo_video_id'] = i0.Variable(livePhotoVideoId.value); } if (visibility.present) { - map['visibility'] = i0.Variable(i1 - .$RemoteAssetEntityTable.$convertervisibility - .toSql(visibility.value)); + map['visibility'] = i0.Variable( + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility.value), + ); } if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); @@ -1292,5 +1566,7 @@ class RemoteAssetEntityCompanion } } -i0.Index get idxRemoteAssetChecksum => i0.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); +i0.Index get idxRemoteAssetChecksum => i0.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', +); diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart index df0390aea0..ff7a3c3444 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -9,51 +9,62 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String primaryAssetId, -}); -typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value primaryAssetId, -}); +typedef $$StackEntityTableCreateCompanionBuilder = + i1.StackEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String primaryAssetId, + }); +typedef $$StackEntityTableUpdateCompanionBuilder = + i1.StackEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value primaryAssetId, + }); -final class $$StackEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> { +final class $$StackEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData + > { $$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('stack_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -67,37 +78,49 @@ class $$StackEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,39 +135,49 @@ class $$StackEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -168,46 +201,58 @@ class $$StackEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, builder: (column) => column); + column: $table.primaryAssetId, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$StackEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$StackEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$StackEntityTableTableManager( - i0.GeneratedDatabase db, i1.$StackEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$StackEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -216,46 +261,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$StackEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value primaryAssetId = const i0.Value.absent(), - }) => - i1.StackEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String primaryAssetId, - }) => - i1.StackEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value primaryAssetId = const i0.Value.absent(), + }) => i1.StackEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) => i1.StackEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$StackEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$StackEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -266,40 +314,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$StackEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$StackEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1.$$StackEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$StackEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$StackEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $StackEntityTable extends i2.StackEntity with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { @@ -310,42 +367,71 @@ class $StackEntityTable extends i2.StackEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _primaryAssetIdMeta = const i0.VerificationMeta('primaryAssetId'); @override late final i0.GeneratedColumn primaryAssetId = - i0.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + i0.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -353,8 +439,9 @@ class $StackEntityTable extends i2.StackEntity static const String $name = 'stack_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -363,24 +450,33 @@ class $StackEntityTable extends i2.StackEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('primary_asset_id')) { context.handle( + _primaryAssetIdMeta, + primaryAssetId.isAcceptableOrUnknown( + data['primary_asset_id']!, _primaryAssetIdMeta, - primaryAssetId.isAcceptableOrUnknown( - data['primary_asset_id']!, _primaryAssetIdMeta)); + ), + ); } else if (isInserting) { context.missing(_primaryAssetIdMeta); } @@ -393,16 +489,26 @@ class $StackEntityTable extends i2.StackEntity i1.StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.StackEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -424,12 +530,13 @@ class StackEntityData extends i0.DataClass final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -441,8 +548,10 @@ class StackEntityData extends i0.DataClass return map; } - factory StackEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -464,19 +573,19 @@ class StackEntityData extends i0.DataClass }; } - i1.StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - i1.StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + i1.StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => i1.StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(i1.StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -534,9 +643,9 @@ class StackEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - primaryAssetId = i0.Value(primaryAssetId); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + primaryAssetId = i0.Value(primaryAssetId); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -553,12 +662,13 @@ class StackEntityCompanion extends i0.UpdateCompanion { }); } - i1.StackEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? primaryAssetId}) { + i1.StackEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? primaryAssetId, + }) { return i1.StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/store.entity.g.dart index b97b5b0a28..7da92cf778 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/store.entity.g.dart @@ -17,17 +17,14 @@ const StoreValueSchema = CollectionSchema( name: r'StoreValue', id: 902899285492123510, properties: { - r'intValue': PropertySchema( - id: 0, - name: r'intValue', - type: IsarType.long, - ), + r'intValue': PropertySchema(id: 0, name: r'intValue', type: IsarType.long), r'strValue': PropertySchema( id: 1, name: r'strValue', type: IsarType.string, - ) + ), }, + estimateSize: _storeValueEstimateSize, serialize: _storeValueSerialize, deserialize: _storeValueDeserialize, @@ -36,6 +33,7 @@ const StoreValueSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _storeValueGetId, getLinks: _storeValueGetLinks, attach: _storeValueAttach, @@ -120,10 +118,7 @@ extension StoreValueQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -149,8 +144,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -158,8 +155,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -174,12 +173,14 @@ extension StoreValueQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -187,12 +188,12 @@ extension StoreValueQueryWhere extension StoreValueQueryFilter on QueryBuilder { QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -201,11 +202,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -214,11 +217,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -229,54 +234,55 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder intValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'intValue'), + ); }); } QueryBuilder - intValueIsNotNull() { + intValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'intValue'), + ); }); } QueryBuilder intValueEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'intValue', value: value), + ); }); } QueryBuilder - intValueGreaterThan( - int? value, { - bool include = false, - }) { + intValueGreaterThan(int? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -285,11 +291,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -300,30 +308,32 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'intValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'intValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder strValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'strValue'), + ); }); } QueryBuilder - strValueIsNotNull() { + strValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'strValue'), + ); }); } @@ -332,27 +342,31 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueGreaterThan( + strValueGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -362,12 +376,14 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -379,28 +395,29 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'strValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'strValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueStartsWith( - String value, { - bool caseSensitive = true, - }) { + strValueStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -409,55 +426,61 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'strValue', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'strValue', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueIsEmpty() { + strValueIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'strValue', value: ''), + ); }); } QueryBuilder - strValueIsNotEmpty() { + strValueIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'strValue', value: ''), + ); }); } } @@ -542,8 +565,9 @@ extension StoreValueQueryWhereDistinct }); } - QueryBuilder distinctByStrValue( - {bool caseSensitive = true}) { + QueryBuilder distinctByStrValue({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index b0c1e6e866..ab5b9a5621 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -43,36 +43,36 @@ class User { }); static User fromDto(UserDto dto) => User( - id: dto.id, - updatedAt: dto.updatedAt, - email: dto.email, - name: dto.name, - isAdmin: dto.isAdmin, - isPartnerSharedBy: dto.isPartnerSharedBy, - isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", - avatarColor: dto.avatarColor, - memoryEnabled: dto.memoryEnabled, - inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, - ); + id: dto.id, + updatedAt: dto.updatedAt, + email: dto.email, + name: dto.name, + isAdmin: dto.isAdmin, + isPartnerSharedBy: dto.isPartnerSharedBy, + isPartnerSharedWith: dto.isPartnerSharedWith, + profileImagePath: dto.profileImagePath ?? "", + avatarColor: dto.avatarColor, + memoryEnabled: dto.memoryEnabled, + inTimeline: dto.inTimeline, + quotaUsageInBytes: dto.quotaUsageInBytes, + quotaSizeInBytes: dto.quotaSizeInBytes, + ); UserDto toDto() => UserDto( - id: id, - email: email, - name: name, - isAdmin: isAdmin, - updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, - avatarColor: avatarColor, - memoryEnabled: memoryEnabled, - inTimeline: inTimeline, - isPartnerSharedBy: isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes, - ); + id: id, + email: email, + name: name, + isAdmin: isAdmin, + updatedAt: updatedAt, + profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + inTimeline: inTimeline, + isPartnerSharedBy: isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes, + ); } class UserEntity extends Table with DriftDefaultsMixin { diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 32be969518..2c3c8a1f9c 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -6,28 +6,28 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; -typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion - Function({ - required String id, - required String name, - i0.Value isAdmin, - required String email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); -typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion - Function({ - i0.Value id, - i0.Value name, - i0.Value isAdmin, - i0.Value email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); +typedef $$UserEntityTableCreateCompanionBuilder = + i1.UserEntityCompanion Function({ + required String id, + required String name, + i0.Value isAdmin, + required String email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + }); +typedef $$UserEntityTableUpdateCompanionBuilder = + i1.UserEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value isAdmin, + i0.Value email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + }); class $$UserEntityTableFilterComposer extends i0.Composer { @@ -39,31 +39,44 @@ class $$UserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnFilters(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnFilters(column)); + column: $table.email, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnFilters(column)); + column: $table.profileImagePath, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column)); + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column)); + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnFilters(column), + ); } class $$UserEntityTableOrderingComposer @@ -76,32 +89,44 @@ class $$UserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnOrderings(column)); + column: $table.email, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.profileImagePath, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$UserEntityTableAnnotationComposer @@ -126,37 +151,51 @@ class $$UserEntityTableAnnotationComposer $composableBuilder(column: $table.email, builder: (column) => column); i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, builder: (column) => column); + column: $table.profileImagePath, + builder: (column) => column, + ); i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, builder: (column) => column); + column: $table.quotaSizeInBytes, + builder: (column) => column, + ); i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, builder: (column) => column); + column: $table.quotaUsageInBytes, + builder: (column) => column, + ); } -class $$UserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( - i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()> { +class $$UserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + > { $$UserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -165,69 +204,75 @@ class $$UserEntityTableTableManager extends i0.RootTableManager< i1.$$UserEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$UserEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value isAdmin = const i0.Value.absent(), - i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value isAdmin = const i0.Value.absent(), - required String email, - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion.insert( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value isAdmin = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => i1.UserEntityCompanion( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value isAdmin = const i0.Value.absent(), + required String email, + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => i1.UserEntityCompanion.insert( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( +typedef $$UserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()>; + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + >; class $UserEntityTable extends i2.UserEntity with i0.TableInfo<$UserEntityTable, i1.UserEntityData> { @@ -238,69 +283,106 @@ class $UserEntityTable extends i2.UserEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isAdminMeta = - const i0.VerificationMeta('isAdmin'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isAdminMeta = const i0.VerificationMeta( + 'isAdmin', + ); @override late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( - 'is_admin', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const i3.Constant(false)); - static const i0.VerificationMeta _emailMeta = - const i0.VerificationMeta('email'); + 'is_admin', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta( + 'email', + ); @override late final i0.GeneratedColumn email = i0.GeneratedColumn( - 'email', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _profileImagePathMeta = const i0.VerificationMeta('profileImagePath'); @override late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn('profile_image_path', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); static const i0.VerificationMeta _quotaSizeInBytesMeta = const i0.VerificationMeta('quotaSizeInBytes'); @override late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _quotaUsageInBytesMeta = const i0.VerificationMeta('quotaUsageInBytes'); @override late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0)); + i0.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i3.Constant(0), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -308,8 +390,9 @@ class $UserEntityTable extends i2.UserEntity static const String $name = 'user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -319,41 +402,58 @@ class $UserEntityTable extends i2.UserEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('is_admin')) { - context.handle(_isAdminMeta, - isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta)); + context.handle( + _isAdminMeta, + isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta), + ); } if (data.containsKey('email')) { context.handle( - _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta)); + _emailMeta, + email.isAcceptableOrUnknown(data['email']!, _emailMeta), + ); } else if (isInserting) { context.missing(_emailMeta); } if (data.containsKey('profile_image_path')) { context.handle( + _profileImagePathMeta, + profileImagePath.isAcceptableOrUnknown( + data['profile_image_path']!, _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, _profileImagePathMeta)); + ), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('quota_size_in_bytes')) { context.handle( + _quotaSizeInBytesMeta, + quotaSizeInBytes.isAcceptableOrUnknown( + data['quota_size_in_bytes']!, _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, _quotaSizeInBytesMeta)); + ), + ); } if (data.containsKey('quota_usage_in_bytes')) { context.handle( + _quotaUsageInBytesMeta, + quotaUsageInBytes.isAcceptableOrUnknown( + data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta)); + ), + ); } return context; } @@ -364,22 +464,38 @@ class $UserEntityTable extends i2.UserEntity i1.UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + i0.DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + i0.DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -404,15 +520,16 @@ class UserEntityData extends i0.DataClass final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -431,8 +548,10 @@ class UserEntityData extends i0.DataClass return map; } - factory UserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -460,29 +579,29 @@ class UserEntityData extends i0.DataClass }; } - i1.UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - i0.Value profileImagePath = const i0.Value.absent(), - DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes}) => - i1.UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + i1.UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + i0.Value profileImagePath = const i0.Value.absent(), + DateTime? updatedAt, + i0.Value quotaSizeInBytes = const i0.Value.absent(), + int? quotaUsageInBytes, + }) => i1.UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -518,8 +637,16 @@ class UserEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -562,9 +689,9 @@ class UserEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), this.quotaSizeInBytes = const i0.Value.absent(), this.quotaUsageInBytes = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - email = i0.Value(email); + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -587,15 +714,16 @@ class UserEntityCompanion extends i0.UpdateCompanion { }); } - i1.UserEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? isAdmin, - i0.Value? email, - i0.Value? profileImagePath, - i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes}) { + i1.UserEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? isAdmin, + i0.Value? email, + i0.Value? profileImagePath, + i0.Value? updatedAt, + i0.Value? quotaSizeInBytes, + i0.Value? quotaUsageInBytes, + }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, diff --git a/mobile/lib/infrastructure/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart index 37a793b2c3..bb87051731 100644 --- a/mobile/lib/infrastructure/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -23,26 +23,14 @@ const UserSchema = CollectionSchema( type: IsarType.byte, enumMap: _UseravatarColorEnumValueMap, ), - r'email': PropertySchema( - id: 1, - name: r'email', - type: IsarType.string, - ), - r'id': PropertySchema( - id: 2, - name: r'id', - type: IsarType.string, - ), + r'email': PropertySchema(id: 1, name: r'email', type: IsarType.string), + r'id': PropertySchema(id: 2, name: r'id', type: IsarType.string), r'inTimeline': PropertySchema( id: 3, name: r'inTimeline', type: IsarType.bool, ), - r'isAdmin': PropertySchema( - id: 4, - name: r'isAdmin', - type: IsarType.bool, - ), + r'isAdmin': PropertySchema(id: 4, name: r'isAdmin', type: IsarType.bool), r'isPartnerSharedBy': PropertySchema( id: 5, name: r'isPartnerSharedBy', @@ -58,11 +46,7 @@ const UserSchema = CollectionSchema( name: r'memoryEnabled', type: IsarType.bool, ), - r'name': PropertySchema( - id: 8, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 8, name: r'name', type: IsarType.string), r'profileImagePath': PropertySchema( id: 9, name: r'profileImagePath', @@ -82,8 +66,9 @@ const UserSchema = CollectionSchema( id: 12, name: r'updatedAt', type: IsarType.dateTime, - ) + ), }, + estimateSize: _userEstimateSize, serialize: _userSerialize, deserialize: _userDeserialize, @@ -100,12 +85,13 @@ const UserSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _userGetId, getLinks: _userGetLinks, attach: _userAttach, @@ -155,7 +141,7 @@ User _userDeserialize( final object = User( avatarColor: _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ?? - AvatarColor.primary, + AvatarColor.primary, email: reader.readString(offsets[1]), id: reader.readString(offsets[2]), inTimeline: reader.readBoolOrNull(offsets[3]) ?? false, @@ -181,7 +167,8 @@ P _userDeserializeProp

( switch (propertyId) { case 0: return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ?? - AvatarColor.primary) as P; + AvatarColor.primary) + as P; case 1: return (reader.readString(offset)) as P; case 2: @@ -311,10 +298,9 @@ extension UserQueryWhereSort on QueryBuilder { extension UserQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -340,8 +326,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -349,8 +337,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -365,21 +355,22 @@ extension UserQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -387,32 +378,40 @@ extension UserQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -420,12 +419,12 @@ extension UserQueryWhere on QueryBuilder { extension UserQueryFilter on QueryBuilder { QueryBuilder avatarColorEqualTo( - AvatarColor value) { + AvatarColor value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'avatarColor', value: value), + ); }); } @@ -434,11 +433,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -447,11 +448,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -462,13 +465,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'avatarColor', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'avatarColor', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -477,11 +482,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -491,12 +498,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -506,12 +515,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -523,14 +534,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'email', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'email', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -539,11 +552,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -552,51 +567,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailContains(String value, - {bool caseSensitive = true}) { + QueryBuilder emailContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder emailMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'email', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'email', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder emailIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'email', value: ''), + ); }); } QueryBuilder emailIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'email', value: ''), + ); }); } @@ -605,11 +628,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -619,12 +644,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,12 +661,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -651,14 +680,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -667,11 +698,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -680,99 +713,105 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder inTimelineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'inTimeline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'inTimeline', value: value), + ); }); } QueryBuilder isAdminEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isAdmin', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isAdmin', value: value), + ); }); } QueryBuilder isPartnerSharedByEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedBy', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedBy', value: value), + ); }); } QueryBuilder isPartnerSharedWithEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedWith', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedWith', value: value), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -781,11 +820,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -794,11 +835,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -809,23 +852,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder memoryEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'memoryEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'memoryEnabled', value: value), + ); }); } @@ -834,11 +879,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -848,12 +895,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -863,12 +912,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -880,14 +931,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -896,11 +949,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -909,51 +964,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } @@ -962,11 +1025,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -976,12 +1041,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -991,12 +1058,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1008,14 +1077,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'profileImagePath', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'profileImagePath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1024,11 +1095,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1037,63 +1110,69 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'profileImagePath', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'profileImagePath', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder profileImagePathIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder quotaSizeInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaSizeInBytes', value: value), + ); }); } @@ -1102,11 +1181,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1115,11 +1196,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1130,23 +1213,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaSizeInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaSizeInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder quotaUsageInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaUsageInBytes', value: value), + ); }); } @@ -1155,11 +1240,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1168,11 +1255,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1183,23 +1272,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaUsageInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaUsageInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -1208,11 +1299,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1221,11 +1314,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1236,13 +1331,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1586,15 +1683,17 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByEmail( - {bool caseSensitive = true}) { + QueryBuilder distinctByEmail({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'email', caseSensitive: caseSensitive); }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -1630,18 +1729,22 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByProfileImagePath( - {bool caseSensitive = true}) { + QueryBuilder distinctByProfileImagePath({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'profileImagePath', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'profileImagePath', + caseSensitive: caseSensitive, + ); }); } diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart index a13ea5c04e..1e9dc8a890 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart @@ -11,51 +11,64 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$UserMetadataEntityTableCreateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - required String userId, - required i2.UserMetadataKey key, - required Map value, -}); -typedef $$UserMetadataEntityTableUpdateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - i0.Value userId, - i0.Value key, - i0.Value> value, -}); +typedef $$UserMetadataEntityTableCreateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }); +typedef $$UserMetadataEntityTableUpdateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + i0.Value userId, + i0.Value key, + i0.Value> value, + }); -final class $$UserMetadataEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData> { +final class $$UserMetadataEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData + > { $$UserMetadataEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet( - 'user_metadata_entity') + 'user_metadata_entity', + ) .userId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -69,35 +82,45 @@ class $$UserMetadataEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get key => $composableBuilder( - column: $table.key, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get key => $composableBuilder( + column: $table.key, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); - i0.ColumnWithTypeConverterFilters, Map, - i3.Uint8List> - get value => $composableBuilder( - column: $table.value, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + i0.ColumnWithTypeConverterFilters< + Map, + Map, + i3.Uint8List + > + get value => $composableBuilder( + column: $table.value, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get userId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,30 +135,39 @@ class $$UserMetadataEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get key => $composableBuilder( - column: $table.key, builder: (column) => i0.ColumnOrderings(column)); + column: $table.key, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get value => $composableBuilder( - column: $table.value, builder: (column) => i0.ColumnOrderings(column)); + column: $table.value, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get userId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -153,89 +185,106 @@ class $$UserMetadataEntityTableAnnotationComposer $composableBuilder(column: $table.key, builder: (column) => column); i0.GeneratedColumnWithTypeConverter, i3.Uint8List> - get value => - $composableBuilder(column: $table.value, builder: (column) => column); + get value => + $composableBuilder(column: $table.value, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get userId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})> { +class $$UserMetadataEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + > { $$UserMetadataEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserMetadataEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserMetadataEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1 .$$UserMetadataEntityTableFilterComposer($db: db, $table: table), createOrderingComposer: () => i1.$$UserMetadataEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$UserMetadataEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value userId = const i0.Value.absent(), - i0.Value key = const i0.Value.absent(), - i0.Value> value = const i0.Value.absent(), - }) => - i1.UserMetadataEntityCompanion( - userId: userId, - key: key, - value: value, - ), - createCompanionCallback: ({ - required String userId, - required i2.UserMetadataKey key, - required Map value, - }) => - i1.UserMetadataEntityCompanion.insert( - userId: userId, - key: key, - value: value, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value userId = const i0.Value.absent(), + i0.Value key = const i0.Value.absent(), + i0.Value> value = const i0.Value.absent(), + }) => i1.UserMetadataEntityCompanion( + userId: userId, + key: key, + value: value, + ), + createCompanionCallback: + ({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }) => i1.UserMetadataEntityCompanion.insert( + userId: userId, + key: key, + value: value, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$UserMetadataEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$UserMetadataEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -246,42 +295,50 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: - i1.$$UserMetadataEntityTableReferences._userIdTable(db), - referencedColumn: i1.$$UserMetadataEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$UserMetadataEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})>; +typedef $$UserMetadataEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + >; class $UserMetadataEntityTable extends i4.UserMetadataEntity with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> { @@ -289,28 +346,46 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $UserMetadataEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter key = - i0.GeneratedColumn('key', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$UserMetadataEntityTable.$converterkey); + i0.GeneratedColumn( + 'key', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$UserMetadataEntityTable.$converterkey, + ); @override - late final i0 - .GeneratedColumnWithTypeConverter, i3.Uint8List> - value = i0.GeneratedColumn('value', aliasedName, false, - type: i0.DriftSqlType.blob, requiredDuringInsert: true) - .withConverter>( - i1.$UserMetadataEntityTable.$convertervalue); + late final i0.GeneratedColumnWithTypeConverter< + Map, + i3.Uint8List + > + value = + i0.GeneratedColumn( + 'value', + aliasedName, + false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + ).withConverter>( + i1.$UserMetadataEntityTable.$convertervalue, + ); @override List get $columns => [userId, key, value]; @override @@ -320,13 +395,16 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static const String $name = 'user_metadata_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -336,18 +414,28 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity @override Set get $primaryKey => {userId, key}; @override - i1.UserMetadataEntityData map(Map data, - {String? tablePrefix}) { + i1.UserMetadataEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: i1.$UserMetadataEntityTable.$converterkey.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}key'])!), + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: i1.$UserMetadataEntityTable.$converterkey.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + ), value: i1.$UserMetadataEntityTable.$convertervalue.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.blob, data['${effectivePrefix}value'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ), ); } @@ -358,9 +446,10 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static i0.JsonTypeConverter2 $converterkey = const i0.EnumIndexConverter( - i2.UserMetadataKey.values); + i2.UserMetadataKey.values, + ); static i0.JsonTypeConverter2, i3.Uint8List, Object?> - $convertervalue = i4.userMetadataConverter; + $convertervalue = i4.userMetadataConverter; @override bool get withoutRowId => true; @override @@ -372,32 +461,41 @@ class UserMetadataEntityData extends i0.DataClass final String userId; final i2.UserMetadataKey key; final Map value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['user_id'] = i0.Variable(userId); { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key), + ); } { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value), + ); } return map; } - factory UserMetadataEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), - key: i1.$UserMetadataEntityTable.$converterkey - .fromJson(serializer.fromJson(json['key'])), - value: i1.$UserMetadataEntityTable.$convertervalue - .fromJson(serializer.fromJson(json['value'])), + key: i1.$UserMetadataEntityTable.$converterkey.fromJson( + serializer.fromJson(json['key']), + ), + value: i1.$UserMetadataEntityTable.$convertervalue.fromJson( + serializer.fromJson(json['value']), + ), ); } @override @@ -405,24 +503,27 @@ class UserMetadataEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), - 'key': serializer - .toJson(i1.$UserMetadataEntityTable.$converterkey.toJson(key)), + 'key': serializer.toJson( + i1.$UserMetadataEntityTable.$converterkey.toJson(key), + ), 'value': serializer.toJson( - i1.$UserMetadataEntityTable.$convertervalue.toJson(value)), + i1.$UserMetadataEntityTable.$convertervalue.toJson(value), + ), }; } - i1.UserMetadataEntityData copyWith( - {String? userId, - i2.UserMetadataKey? key, - Map? value}) => - i1.UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + i1.UserMetadataEntityData copyWith({ + String? userId, + i2.UserMetadataKey? key, + Map? value, + }) => i1.UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion( - i1.UserMetadataEntityCompanion data) { + i1.UserMetadataEntityCompanion data, + ) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, key: data.key.present ? data.key.value : this.key, @@ -465,9 +566,9 @@ class UserMetadataEntityCompanion required String userId, required i2.UserMetadataKey key, required Map value, - }) : userId = i0.Value(userId), - key = i0.Value(key), - value = i0.Value(value); + }) : userId = i0.Value(userId), + key = i0.Value(key), + value = i0.Value(value); static i0.Insertable custom({ i0.Expression? userId, i0.Expression? key, @@ -480,10 +581,11 @@ class UserMetadataEntityCompanion }); } - i1.UserMetadataEntityCompanion copyWith( - {i0.Value? userId, - i0.Value? key, - i0.Value>? value}) { + i1.UserMetadataEntityCompanion copyWith({ + i0.Value? userId, + i0.Value? key, + i0.Value>? value, + }) { return i1.UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -499,11 +601,13 @@ class UserMetadataEntityCompanion } if (key.present) { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key.value)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key.value), + ); } if (value.present) { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart index e8bf9ace43..6c81c7ff7f 100644 --- a/mobile/lib/infrastructure/repositories/asset_media.repository.dart +++ b/mobile/lib/infrastructure/repositories/asset_media.repository.dart @@ -6,21 +6,13 @@ import 'package:photo_manager/photo_manager.dart'; class AssetMediaRepository { const AssetMediaRepository(); - Future getThumbnail( - String id, { - int quality = 80, - Size size = const Size.square(256), - }) => - AssetEntity( - id: id, - // The below fields are not used in thumbnailDataWithSize but are required - // to create an AssetEntity instance. It is faster to create a dummy AssetEntity - // instance than to fetch the asset from the device first. - typeInt: AssetType.image.index, - width: size.width.toInt(), - height: size.height.toInt(), - ).thumbnailDataWithSize( - ThumbnailSize(size.width.toInt(), size.height.toInt()), - quality: quality, - ); + Future getThumbnail(String id, {int quality = 80, Size size = const Size.square(256)}) => AssetEntity( + id: id, + // The below fields are not used in thumbnailDataWithSize but are required + // to create an AssetEntity instance. It is faster to create a dummy AssetEntity + // instance than to fetch the asset from the device first. + typeInt: AssetType.image.index, + width: size.width.toInt(), + height: size.height.toInt(), + ).thumbnailDataWithSize(ThumbnailSize(size.width.toInt(), size.height.toInt()), quality: quality); } diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index aaba90de5f..ce38ff9311 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -24,9 +24,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { useColumns: false, ), ]) - ..where( - _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded)); } Future getTotalCount() async { @@ -79,9 +77,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns( - [_db.localAlbumAssetEntity.assetId], - ) + ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ innerJoin( _db.localAlbumEntity, @@ -112,9 +108,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) - ..where( - _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); final query = _db.localAssetEntity.select() ..where( @@ -138,11 +132,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ) & lae.id.isNotInQuery(_getExcludedSubquery()), ) - ..orderBy( - [ - (localAsset) => OrderingTerm.desc(localAsset.createdAt), - ], - ); + ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]); return query.map((localAsset) => localAsset.toDto()).get(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 15fce4d649..7f6374ed24 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -59,66 +59,58 @@ class IsarDatabaseRepository implements IDatabaseRepository { PersonEntity, AssetFaceEntity, ], - include: { - 'package:immich_mobile/infrastructure/entities/merged_asset.drift', - }, + include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift implements IDatabaseRepository { Drift([QueryExecutor? executor]) - : super( - executor ?? - driftDatabase( - name: 'immich', - native: const DriftNativeOptions(shareAcrossIsolates: true), - ), - ); + : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override int get schemaVersion => 4; @override MigrationStrategy get migration => MigrationStrategy( - onUpgrade: (m, from, to) async { - // Run migration steps without foreign keys and re-enable them later - await customStatement('PRAGMA foreign_keys = OFF'); + onUpgrade: (m, from, to) async { + // Run migration steps without foreign keys and re-enable them later + await customStatement('PRAGMA foreign_keys = OFF'); - await m.runMigrationSteps( - from: from, - to: to, - steps: migrationSteps( - from1To2: (m, v2) async { - for (final entity in v2.entities) { - await m.drop(entity); - await m.create(entity); - } - }, - from2To3: (m, v3) async { - // Removed foreign key constraint on stack.primaryAssetId - await m.alterTable(TableMigration(v3.stackEntity)); - }, - from3To4: (m, v4) async { - // Thumbnail path column got removed from person_entity - await m.alterTable(TableMigration(v4.personEntity)); - // asset_face_entity is added - await m.create(v4.assetFaceEntity); - }, - ), - ); - - if (kDebugMode) { - // Fail if the migration broke foreign keys - final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); - assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); - } - - await customStatement('PRAGMA foreign_keys = ON;'); - }, - beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON'); - await customStatement('PRAGMA synchronous = NORMAL'); - await customStatement('PRAGMA journal_mode = WAL'); - }, + await m.runMigrationSteps( + from: from, + to: to, + steps: migrationSteps( + from1To2: (m, v2) async { + for (final entity in v2.entities) { + await m.drop(entity); + await m.create(entity); + } + }, + from2To3: (m, v3) async { + // Removed foreign key constraint on stack.primaryAssetId + await m.alterTable(TableMigration(v3.stackEntity)); + }, + from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity + await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added + await m.create(v4.assetFaceEntity); + }, + ), ); + + if (kDebugMode) { + // Fail if the migration broke foreign keys + final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); + assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); + } + + await customStatement('PRAGMA foreign_keys = ON;'); + }, + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + }, + ); } class DriftDatabaseRepository implements IDatabaseRepository { diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index f5962f09ab..296b87900e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -41,213 +41,232 @@ abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); $DriftManager get managers => $DriftManager(this); late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); - late final i2.$RemoteAssetEntityTable remoteAssetEntity = - i2.$RemoteAssetEntityTable(this); + late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2 + .$RemoteAssetEntityTable(this); late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); - late final i4.$LocalAssetEntityTable localAssetEntity = - i4.$LocalAssetEntityTable(this); - late final i5.$LocalAlbumEntityTable localAlbumEntity = - i5.$LocalAlbumEntityTable(this); - late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i6.$LocalAlbumAssetEntityTable(this); - late final i7.$UserMetadataEntityTable userMetadataEntity = - i7.$UserMetadataEntityTable(this); - late final i8.$PartnerEntityTable partnerEntity = - i8.$PartnerEntityTable(this); - late final i9.$RemoteExifEntityTable remoteExifEntity = - i9.$RemoteExifEntityTable(this); - late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = - i10.$RemoteAlbumEntityTable(this); - late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = - i11.$RemoteAlbumAssetEntityTable(this); - late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = - i12.$RemoteAlbumUserEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = i4 + .$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = i5 + .$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i6 + .$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = i7 + .$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = i8.$PartnerEntityTable( + this, + ); + late final i9.$RemoteExifEntityTable remoteExifEntity = i9 + .$RemoteExifEntityTable(this); + late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = i10 + .$RemoteAlbumEntityTable(this); + late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i11 + .$RemoteAlbumAssetEntityTable(this); + late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i12 + .$RemoteAlbumUserEntityTable(this); late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this); - late final i14.$MemoryAssetEntityTable memoryAssetEntity = - i14.$MemoryAssetEntityTable(this); + late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14 + .$MemoryAssetEntityTable(this); late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); - late final i16.$AssetFaceEntityTable assetFaceEntity = - i16.$AssetFaceEntityTable(this); - i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer(this) - .accessor(i17.MergedAssetDrift.new); + late final i16.$AssetFaceEntityTable assetFaceEntity = i16 + .$AssetFaceEntityTable(this); + i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer( + this, + ).accessor(i17.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumEntity, - localAlbumAssetEntity, - i4.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, - i2.idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity, - assetFaceEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, + i2.uQRemoteAssetOwnerChecksum, + i2.idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; @override - i0.StreamQueryUpdateRules get streamUpdateRules => - const i0.StreamQueryUpdateRules( - [ - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('user_metadata_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('memory_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('person_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update), - ], - ), - ], - ); + i0.StreamQueryUpdateRules + get streamUpdateRules => const i0.StreamQueryUpdateRules([ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('user_metadata_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'memory_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'person_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)], + ), + ]); @override i0.DriftDatabaseOptions get options => const i0.DriftDatabaseOptions(storeDateTimeAsText: true); @@ -278,7 +297,9 @@ class $DriftManager { i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity); i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => i11.$$RemoteAlbumAssetEntityTableTableManager( - _db, _db.remoteAlbumAssetEntity); + _db, + _db.remoteAlbumAssetEntity, + ); i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12 .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); i13.$$MemoryEntityTableTableManager get memoryEntity => diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 57c90f731d..5bf20780f4 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -29,696 +29,1181 @@ final class Schema2 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_24, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_24], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape0 extends i0.VersionedTable { Shape0({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get isAdmin => columnsByName['is_admin']! as i1.GeneratedColumn; - i1.GeneratedColumn get email => columnsByName['email']! as i1.GeneratedColumn; - i1.GeneratedColumn get profileImagePath => columnsByName['profile_image_path']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get quotaSizeInBytes => columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn; - i1.GeneratedColumn get quotaUsageInBytes => columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileImagePath => + columnsByName['profile_image_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaSizeInBytes => + columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaUsageInBytes => + columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_0(String aliasedName) => - i1.GeneratedColumn('id', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_1(String aliasedName) => - i1.GeneratedColumn('name', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_2(String aliasedName) => i1.GeneratedColumn('is_admin', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'name', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_2(String aliasedName) => + i1.GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_3(String aliasedName) => - i1.GeneratedColumn('email', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'email', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_4(String aliasedName) => - i1.GeneratedColumn('profile_image_path', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_5(String aliasedName) => - i1.GeneratedColumn('updated_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_6(String aliasedName) => - i1.GeneratedColumn('quota_size_in_bytes', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_7(String aliasedName) => - i1.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape1 extends i0.VersionedTable { Shape1({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get durationInSeconds => columnsByName['duration_in_seconds']! as i1.GeneratedColumn; - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get checksum => columnsByName['checksum']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get localDateTime => columnsByName['local_date_time']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbHash => columnsByName['thumb_hash']! as i1.GeneratedColumn; - i1.GeneratedColumn get deletedAt => columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; i1.GeneratedColumn get livePhotoVideoId => columnsByName['live_photo_video_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get visibility => columnsByName['visibility']! as i1.GeneratedColumn; - i1.GeneratedColumn get stackId => columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_8(String aliasedName) => - i1.GeneratedColumn('type', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'type', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_9(String aliasedName) => - i1.GeneratedColumn('created_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_10(String aliasedName) => - i1.GeneratedColumn('width', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'width', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_11(String aliasedName) => - i1.GeneratedColumn('height', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'height', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_12(String aliasedName) => - i1.GeneratedColumn('duration_in_seconds', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_13(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_14(String aliasedName) => i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); -i1.GeneratedColumn _column_15(String aliasedName) => i1.GeneratedColumn('owner_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'checksum', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_14(String aliasedName) => + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_15(String aliasedName) => + i1.GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_16(String aliasedName) => - i1.GeneratedColumn('local_date_time', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_17(String aliasedName) => - i1.GeneratedColumn('thumb_hash', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_18(String aliasedName) => - i1.GeneratedColumn('deleted_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_19(String aliasedName) => - i1.GeneratedColumn('live_photo_video_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_20(String aliasedName) => - i1.GeneratedColumn('visibility', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_21(String aliasedName) => - i1.GeneratedColumn('stack_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape2 extends i0.VersionedTable { Shape2({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get durationInSeconds => columnsByName['duration_in_seconds']! as i1.GeneratedColumn; - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get checksum => columnsByName['checksum']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get orientation => columnsByName['orientation']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get orientation => + columnsByName['orientation']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_22(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, true, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_23(String aliasedName) => i1.GeneratedColumn('orientation', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'checksum', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_23(String aliasedName) => + i1.GeneratedColumn( + 'orientation', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape3 extends i0.VersionedTable { Shape3({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get primaryAssetId => columnsByName['primary_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get primaryAssetId => + columnsByName['primary_asset_id']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_24(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); class Shape4 extends i0.VersionedTable { Shape4({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get userId => columnsByName['user_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get key => columnsByName['key']! as i1.GeneratedColumn; - i1.GeneratedColumn get value => columnsByName['value']! as i1.GeneratedColumn; + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get key => + columnsByName['key']! as i1.GeneratedColumn; + i1.GeneratedColumn get value => + columnsByName['value']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_25(String aliasedName) => i1.GeneratedColumn('user_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_25(String aliasedName) => + i1.GeneratedColumn( + 'user_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_26(String aliasedName) => - i1.GeneratedColumn('key', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'key', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_27(String aliasedName) => - i1.GeneratedColumn('value', aliasedName, false, type: i1.DriftSqlType.blob); + i1.GeneratedColumn( + 'value', + aliasedName, + false, + type: i1.DriftSqlType.blob, + ); class Shape5 extends i0.VersionedTable { Shape5({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get sharedById => columnsByName['shared_by_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get sharedWithId => columnsByName['shared_with_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get inTimeline => columnsByName['in_timeline']! as i1.GeneratedColumn; + i1.GeneratedColumn get sharedById => + columnsByName['shared_by_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get sharedWithId => + columnsByName['shared_with_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get inTimeline => + columnsByName['in_timeline']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_28(String aliasedName) => - i1.GeneratedColumn('shared_by_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_29(String aliasedName) => - i1.GeneratedColumn('shared_with_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); -i1.GeneratedColumn _column_30(String aliasedName) => i1.GeneratedColumn('in_timeline', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); +i1.GeneratedColumn _column_30(String aliasedName) => + i1.GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); class Shape6 extends i0.VersionedTable { Shape6({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get backupSelection => columnsByName['backup_selection']! as i1.GeneratedColumn; - i1.GeneratedColumn get isIosSharedAlbum => columnsByName['is_ios_shared_album']! as i1.GeneratedColumn; - i1.GeneratedColumn get marker_ => columnsByName['marker']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get backupSelection => + columnsByName['backup_selection']! as i1.GeneratedColumn; + i1.GeneratedColumn get isIosSharedAlbum => + columnsByName['is_ios_shared_album']! as i1.GeneratedColumn; + i1.GeneratedColumn get marker_ => + columnsByName['marker']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_31(String aliasedName) => - i1.GeneratedColumn('backup_selection', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_32(String aliasedName) => - i1.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); -i1.GeneratedColumn _column_33(String aliasedName) => i1.GeneratedColumn('marker', aliasedName, true, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + i1.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_33(String aliasedName) => + i1.GeneratedColumn( + 'marker', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); class Shape7 extends i0.VersionedTable { Shape7({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get albumId => columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_34(String aliasedName) => i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); -i1.GeneratedColumn _column_35(String aliasedName) => i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_34(String aliasedName) => + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); +i1.GeneratedColumn _column_35(String aliasedName) => + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape8 extends i0.VersionedTable { Shape8({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get city => columnsByName['city']! as i1.GeneratedColumn; - i1.GeneratedColumn get state => columnsByName['state']! as i1.GeneratedColumn; - i1.GeneratedColumn get country => columnsByName['country']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get city => + columnsByName['city']! as i1.GeneratedColumn; + i1.GeneratedColumn get state => + columnsByName['state']! as i1.GeneratedColumn; + i1.GeneratedColumn get country => + columnsByName['country']! as i1.GeneratedColumn; i1.GeneratedColumn get dateTimeOriginal => columnsByName['date_time_original']! as i1.GeneratedColumn; - i1.GeneratedColumn get description => columnsByName['description']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get exposureTime => columnsByName['exposure_time']! as i1.GeneratedColumn; - i1.GeneratedColumn get fNumber => columnsByName['f_number']! as i1.GeneratedColumn; - i1.GeneratedColumn get fileSize => columnsByName['file_size']! as i1.GeneratedColumn; - i1.GeneratedColumn get focalLength => columnsByName['focal_length']! as i1.GeneratedColumn; - i1.GeneratedColumn get latitude => columnsByName['latitude']! as i1.GeneratedColumn; - i1.GeneratedColumn get longitude => columnsByName['longitude']! as i1.GeneratedColumn; - i1.GeneratedColumn get iso => columnsByName['iso']! as i1.GeneratedColumn; - i1.GeneratedColumn get make => columnsByName['make']! as i1.GeneratedColumn; - i1.GeneratedColumn get model => columnsByName['model']! as i1.GeneratedColumn; - i1.GeneratedColumn get lens => columnsByName['lens']! as i1.GeneratedColumn; - i1.GeneratedColumn get orientation => columnsByName['orientation']! as i1.GeneratedColumn; - i1.GeneratedColumn get timeZone => columnsByName['time_zone']! as i1.GeneratedColumn; - i1.GeneratedColumn get rating => columnsByName['rating']! as i1.GeneratedColumn; - i1.GeneratedColumn get projectionType => columnsByName['projection_type']! as i1.GeneratedColumn; + i1.GeneratedColumn get description => + columnsByName['description']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get exposureTime => + columnsByName['exposure_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get fNumber => + columnsByName['f_number']! as i1.GeneratedColumn; + i1.GeneratedColumn get fileSize => + columnsByName['file_size']! as i1.GeneratedColumn; + i1.GeneratedColumn get focalLength => + columnsByName['focal_length']! as i1.GeneratedColumn; + i1.GeneratedColumn get latitude => + columnsByName['latitude']! as i1.GeneratedColumn; + i1.GeneratedColumn get longitude => + columnsByName['longitude']! as i1.GeneratedColumn; + i1.GeneratedColumn get iso => + columnsByName['iso']! as i1.GeneratedColumn; + i1.GeneratedColumn get make => + columnsByName['make']! as i1.GeneratedColumn; + i1.GeneratedColumn get model => + columnsByName['model']! as i1.GeneratedColumn; + i1.GeneratedColumn get lens => + columnsByName['lens']! as i1.GeneratedColumn; + i1.GeneratedColumn get orientation => + columnsByName['orientation']! as i1.GeneratedColumn; + i1.GeneratedColumn get timeZone => + columnsByName['time_zone']! as i1.GeneratedColumn; + i1.GeneratedColumn get rating => + columnsByName['rating']! as i1.GeneratedColumn; + i1.GeneratedColumn get projectionType => + columnsByName['projection_type']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_36(String aliasedName) => i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_36(String aliasedName) => + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_37(String aliasedName) => - i1.GeneratedColumn('city', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'city', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_38(String aliasedName) => - i1.GeneratedColumn('state', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'state', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_39(String aliasedName) => - i1.GeneratedColumn('country', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'country', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_40(String aliasedName) => - i1.GeneratedColumn('date_time_original', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_41(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'description', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_42(String aliasedName) => - i1.GeneratedColumn('exposure_time', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_43(String aliasedName) => - i1.GeneratedColumn('f_number', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'f_number', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_44(String aliasedName) => - i1.GeneratedColumn('file_size', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'file_size', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_45(String aliasedName) => - i1.GeneratedColumn('focal_length', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_46(String aliasedName) => - i1.GeneratedColumn('latitude', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'latitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_47(String aliasedName) => - i1.GeneratedColumn('longitude', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'longitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_48(String aliasedName) => - i1.GeneratedColumn('iso', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'iso', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_49(String aliasedName) => - i1.GeneratedColumn('make', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'make', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_50(String aliasedName) => - i1.GeneratedColumn('model', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'model', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_51(String aliasedName) => - i1.GeneratedColumn('lens', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'lens', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_52(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_53(String aliasedName) => - i1.GeneratedColumn('time_zone', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_54(String aliasedName) => - i1.GeneratedColumn('rating', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'rating', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_55(String aliasedName) => - i1.GeneratedColumn('projection_type', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape9 extends i0.VersionedTable { Shape9({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get description => columnsByName['description']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbnailAssetId => columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get isActivityEnabled => columnsByName['is_activity_enabled']! as i1.GeneratedColumn; - i1.GeneratedColumn get order => columnsByName['order']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get description => + columnsByName['description']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailAssetId => + columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isActivityEnabled => + columnsByName['is_activity_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get order => + columnsByName['order']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_56(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, false, - type: i1.DriftSqlType.string, defaultValue: const CustomExpression('\'\'')); + i1.GeneratedColumn( + 'description', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultValue: const CustomExpression('\'\''), + ); i1.GeneratedColumn _column_57(String aliasedName) => - i1.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: - i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i1.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_58(String aliasedName) => - i1.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + i1.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); i1.GeneratedColumn _column_59(String aliasedName) => - i1.GeneratedColumn('order', aliasedName, false, type: i1.DriftSqlType.int); -i1.GeneratedColumn _column_60(String aliasedName) => i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'order', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_60(String aliasedName) => + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape10 extends i0.VersionedTable { Shape10({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get albumId => columnsByName['album_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get userId => columnsByName['user_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get role => columnsByName['role']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get role => + columnsByName['role']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_61(String aliasedName) => - i1.GeneratedColumn('role', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'role', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); class Shape11 extends i0.VersionedTable { Shape11({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get deletedAt => columnsByName['deleted_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get data => columnsByName['data']! as i1.GeneratedColumn; - i1.GeneratedColumn get isSaved => columnsByName['is_saved']! as i1.GeneratedColumn; - i1.GeneratedColumn get memoryAt => columnsByName['memory_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get seenAt => columnsByName['seen_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get showAt => columnsByName['show_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get hideAt => columnsByName['hide_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get data => + columnsByName['data']! as i1.GeneratedColumn; + i1.GeneratedColumn get isSaved => + columnsByName['is_saved']! as i1.GeneratedColumn; + i1.GeneratedColumn get memoryAt => + columnsByName['memory_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get seenAt => + columnsByName['seen_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get showAt => + columnsByName['show_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get hideAt => + columnsByName['hide_at']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_62(String aliasedName) => - i1.GeneratedColumn('data', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_63(String aliasedName) => i1.GeneratedColumn('is_saved', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'data', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_63(String aliasedName) => + i1.GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_64(String aliasedName) => - i1.GeneratedColumn('memory_at', aliasedName, false, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_65(String aliasedName) => - i1.GeneratedColumn('seen_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_66(String aliasedName) => - i1.GeneratedColumn('show_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'show_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_67(String aliasedName) => - i1.GeneratedColumn('hide_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); class Shape12 extends i0.VersionedTable { Shape12({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get memoryId => columnsByName['memory_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get memoryId => + columnsByName['memory_id']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_68(String aliasedName) => i1.GeneratedColumn('memory_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_68(String aliasedName) => + i1.GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); class Shape13 extends i0.VersionedTable { Shape13({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get faceAssetId => columnsByName['face_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbnailPath => columnsByName['thumbnail_path']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get isHidden => columnsByName['is_hidden']! as i1.GeneratedColumn; - i1.GeneratedColumn get color => columnsByName['color']! as i1.GeneratedColumn; - i1.GeneratedColumn get birthDate => columnsByName['birth_date']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailPath => + columnsByName['thumbnail_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_69(String aliasedName) => - i1.GeneratedColumn('face_asset_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_70(String aliasedName) => - i1.GeneratedColumn('thumbnail_path', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_71(String aliasedName) => i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); -i1.GeneratedColumn _column_72(String aliasedName) => i1.GeneratedColumn('is_hidden', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + i1.GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_71(String aliasedName) => + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_72(String aliasedName) => + i1.GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); i1.GeneratedColumn _column_73(String aliasedName) => - i1.GeneratedColumn('color', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'color', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_74(String aliasedName) => - i1.GeneratedColumn('birth_date', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); final class Schema3 extends i0.VersionedSchema { Schema3({required super.database}) : super(version: 3); @@ -744,326 +1229,295 @@ final class Schema3 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_75, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } i1.GeneratedColumn _column_75(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); final class Schema4 extends i0.VersionedSchema { Schema4({required super.database}) : super(version: 4); @@ -1090,391 +1544,416 @@ final class Schema4 extends i0.VersionedSchema { assetFaceEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_75, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape14 personEntity = Shape14( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape15 assetFaceEntity = Shape15( - source: i0.VersionedTable( - entityName: 'asset_face_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_36, - _column_76, - _column_77, - _column_78, - _column_79, - _column_80, - _column_81, - _column_82, - _column_83, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape14 extends i0.VersionedTable { Shape14({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get faceAssetId => columnsByName['face_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get isHidden => columnsByName['is_hidden']! as i1.GeneratedColumn; - i1.GeneratedColumn get color => columnsByName['color']! as i1.GeneratedColumn; - i1.GeneratedColumn get birthDate => columnsByName['birth_date']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; } class Shape15 extends i0.VersionedTable { Shape15({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get personId => columnsByName['person_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get imageWidth => columnsByName['image_width']! as i1.GeneratedColumn; - i1.GeneratedColumn get imageHeight => columnsByName['image_height']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxX1 => columnsByName['bounding_box_x1']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxY1 => columnsByName['bounding_box_y1']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxX2 => columnsByName['bounding_box_x2']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxY2 => columnsByName['bounding_box_y2']! as i1.GeneratedColumn; - i1.GeneratedColumn get sourceType => columnsByName['source_type']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_76(String aliasedName) => i1.GeneratedColumn('person_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES person_entity (id) ON DELETE SET NULL')); +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_77(String aliasedName) => - i1.GeneratedColumn('image_width', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_78(String aliasedName) => - i1.GeneratedColumn('image_height', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_79(String aliasedName) => - i1.GeneratedColumn('bounding_box_x1', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_80(String aliasedName) => - i1.GeneratedColumn('bounding_box_y1', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_81(String aliasedName) => - i1.GeneratedColumn('bounding_box_x2', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_82(String aliasedName) => - i1.GeneratedColumn('bounding_box_y2', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_83(String aliasedName) => - i1.GeneratedColumn('source_type', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -1507,10 +1986,10 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, -}) => - i0.VersionedSchema.stepByStepHelper( - step: migrationSteps( - from1To2: from1To2, - from2To3: from2To3, - from3To4: from3To4, - )); +}) => i0.VersionedSchema.stepByStepHelper( + step: migrationSteps( + from1To2: from1To2, + from2To3: from2To3, + from3To4: from3To4, + ), +); diff --git a/mobile/lib/infrastructure/repositories/exif.repository.dart b/mobile/lib/infrastructure/repositories/exif.repository.dart index 726e51b77d..0ede30680e 100644 --- a/mobile/lib/infrastructure/repositories/exif.repository.dart +++ b/mobile/lib/infrastructure/repositories/exif.repository.dart @@ -33,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository { Future> updateAll(List exifInfos) { return transaction(() async { - await _db.exifInfos.putAll( - exifInfos.map(entity.ExifInfo.fromDto).toList(), - ); + await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList()); return exifInfos; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index c1e5724b4c..feb25925f8 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -14,8 +14,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; final Platform _platform; const DriftLocalAlbumRepository(this._db, {Platform? platform}) - : _platform = platform ?? const LocalPlatform(), - super(_db); + : _platform = platform ?? const LocalPlatform(), + super(_db); Future> getAll({Set sortBy = const {}}) { final assetCount = _db.localAlbumAssetEntity.assetId.count(); @@ -34,41 +34,32 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), - SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), - SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), - SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), - SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), - }, - ); + orderings.add(switch (sort) { + SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), + SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), + SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), + SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), + SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), + }); } query.orderBy(orderings); } - return query - .map( - (row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0), - ) - .get(); + return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get(); } Future delete(String albumId) => transaction(() async { - // Remove all assets that are only in this particular album - // We cannot remove all assets in the album because they might be in other albums in iOS - // That is not the case on Android since asset <-> album has one:one mapping - final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); - await _deleteAssets(assetsToDelete); + // Remove all assets that are only in this particular album + // We cannot remove all assets in the album because they might be in other albums in iOS + // That is not the case on Android since asset <-> album has one:one mapping + final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); + await _deleteAssets(assetsToDelete); - // All the other assets that are still associated will be unlinked automatically on-cascade - await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); - }); + // All the other assets that are still associated will be unlinked automatically on-cascade + await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); + }); - Future syncDeletes( - String albumId, - Iterable assetIdsToKeep, - ) async { + Future syncDeletes(String albumId, Iterable assetIdsToKeep) async { if (assetIdsToKeep.isEmpty) { return Future.value(); } @@ -77,12 +68,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { deleteSmt.where((localAsset) { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - ), - ]); + ..join([innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id))]); subQuery.where( _db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), ); @@ -109,12 +95,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (toUpsert.isNotEmpty) { await _upsertAssets(toUpsert); await _db.localAlbumAssetEntity.insertAll( - toUpsert.map( - (a) => LocalAlbumAssetEntityCompanion.insert( - assetId: a.id, - albumId: localAlbum.id, - ), - ), + toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)), mode: InsertMode.insertOrIgnore, ); } @@ -162,10 +143,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - ), + innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)), ]); subQuery.where(_db.localAlbumEntity.marker_.isNotNull()); return localAsset.id.isInQuery(subQuery); @@ -178,16 +156,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssets(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } @@ -224,11 +198,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insertAll( _db.localAlbumAssetEntity, albumIds.cast().nonNulls.map( - (albumId) => LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), - ), + (albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId), + ), onConflict: DoNothing(), ); }); @@ -237,18 +208,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssetsToHash(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where( - _db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull(), - ) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull()) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } @@ -275,10 +240,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, companion, - onConflict: DoUpdate( - (_) => companion, - where: (old) => old.updatedAt.isNotValue(asset.updatedAt), - ), + onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)), ); } }); @@ -351,15 +313,13 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future getThumbnail(String albumId) async { - final query = _db.localAlbumAssetEntity.select().join([ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) - ..limit(1); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) + ..limit(1); final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 17521e1cba..58adac30db 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -16,14 +16,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), useColumns: false, ), - ]) - ..where(_db.localAssetEntity.id.equals(id)); + ])..where(_db.localAssetEntity.id.equals(id)); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index 3663b75bf3..2a52faf2dd 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -13,30 +13,21 @@ class DriftMemoryRepository extends DriftDatabaseRepository { final now = DateTime.now(); final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.ownerId.equals(ownerId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..where( - _db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), - ) - ..where( - _db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc), - ) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.ownerId.equals(ownerId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc)) + ..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc)) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); @@ -59,24 +50,19 @@ class DriftMemoryRepository extends DriftDatabaseRepository { } Future get(String memoryId) async { - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.id.equals(memoryId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.id.equals(memoryId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart index 9e78b1b65a..b12061ad24 100644 --- a/mobile/lib/infrastructure/repositories/partner.repository.dart +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -9,24 +9,13 @@ class DriftPartnerRepository extends DriftDatabaseRepository { Future> getPartners(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } @@ -35,60 +24,33 @@ class DriftPartnerRepository extends DriftDatabaseRepository { final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not()); return query.map((user) { - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: false, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false); }).get(); } // Get users who are sharing their photos WITH the current user Future> getSharedWith(String partnerId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(partnerId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(partnerId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } // Get users who the current user is sharing their photos TO Future> getSharedBy(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)), + ])..where(_db.partnerEntity.sharedById.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } @@ -108,35 +70,24 @@ class DriftPartnerRepository extends DriftDatabaseRepository { Future getPartner(String partnerId, String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).getSingleOrNull(); } Future toggleShowInTimeline(PartnerUserDto partner, String userId) { return _db.partnerEntity.update().replace( - PartnerEntityCompanion( - sharedById: Value(partner.id), - sharedWithId: Value(userId), - inTimeline: Value(!partner.inTimeline), - ), - ); + PartnerEntityCompanion( + sharedById: Value(partner.id), + sharedWithId: Value(userId), + inTimeline: Value(!partner.inTimeline), + ), + ); } Future create(String partnerId, String userId) { @@ -150,8 +101,6 @@ class DriftPartnerRepository extends DriftDatabaseRepository { } Future delete(String partnerId, String userId) { - return _db.partnerEntity.deleteWhere( - (t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId), - ); + return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId)); } } diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index f60caceb47..79f3d78fd7 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -16,9 +16,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { final Drift _db; const DriftRemoteAlbumRepository(this._db) : super(_db); - Future> getAll({ - Set sortBy = const {SortRemoteAlbumsBy.updatedAt}, - }) { + Future> getAll({Set sortBy = const {SortRemoteAlbumsBy.updatedAt}}) { final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); final query = _db.remoteAlbumEntity.select().join([ @@ -32,11 +30,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), useColumns: false, ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), + leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false), ]); query ..where(_db.remoteAssetEntity.deletedAt.isNull()) @@ -47,22 +41,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), - SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), - }, - ); + orderings.add(switch (sort) { + SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), + SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), + }); } query.orderBy(orderings); } return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( - assetCount: row.read(assetCount) ?? 0, - ownerName: row.read(_db.userEntity.name)!, - ), + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), ) .get(); } @@ -70,42 +61,39 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future get(String albumId) { final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) - ..addColumns([assetCount]) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) + ..addColumns([assetCount]) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( - assetCount: row.read(assetCount) ?? 0, - ownerName: row.read(_db.userEntity.name)!, - ), + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), ) .getSingleOrNull(); } - Future create( - RemoteAlbum album, - List assetIds, - ) async { + Future create(RemoteAlbum album, List assetIds) async { await _db.transaction(() async { final entity = RemoteAlbumEntityCompanion( id: Value(album.id), @@ -123,17 +111,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (assetIds.isNotEmpty) { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(album.id), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); } }); @@ -141,38 +123,30 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future update(RemoteAlbum album) async { await _db.remoteAlbumEntity.update().replace( - RemoteAlbumEntityCompanion( - id: Value(album.id), - name: Value(album.name), - ownerId: Value(album.ownerId), - createdAt: Value(album.createdAt), - updatedAt: Value(album.updatedAt), - description: Value(album.description), - thumbnailAssetId: Value(album.thumbnailAssetId), - isActivityEnabled: Value(album.isActivityEnabled), - order: Value(album.order), - ), - ); + RemoteAlbumEntityCompanion( + id: Value(album.id), + name: Value(album.name), + ownerId: Value(album.ownerId), + createdAt: Value(album.createdAt), + updatedAt: Value(album.updatedAt), + description: Value(album.description), + thumbnailAssetId: Value(album.thumbnailAssetId), + isActivityEnabled: Value(album.isActivityEnabled), + order: Value(album.order), + ), + ); } Future removeAssets(String albumId, List assetIds) { - return _db.remoteAlbumAssetEntity.deleteWhere( - (tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds), - ); + return _db.remoteAlbumAssetEntity.deleteWhere((tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds)); } FutureOr<(DateTime, DateTime)> getDateRange(String albumId) { final query = _db.remoteAlbumAssetEntity.selectOnly() ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) - ..addColumns([ - _db.remoteAssetEntity.createdAt.min(), - _db.remoteAssetEntity.createdAt.max(), - ]) + ..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()]) ..join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), ]); return query.map((row) { @@ -183,8 +157,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { } Future> getSharedUsers(String albumId) async { - final albumUserRows = - await (_db.select(_db.remoteAlbumUserEntity)..where((row) => row.albumId.equals(albumId))).get(); + final albumUserRows = await (_db.select( + _db.remoteAlbumUserEntity, + )..where((row) => row.albumId.equals(albumId))).get(); if (albumUserRows.isEmpty) { return []; @@ -214,29 +189,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future> getAssets(String albumId) { final query = _db.remoteAlbumAssetEntity.select().join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), - ]) - ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } Future addAssets(String albumId, List assetIds) async { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(albumId), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); return assetIds.length; @@ -252,47 +217,41 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { ); return _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumUserEntity, - albumUsers, - ); + batch.insertAll(_db.remoteAlbumUserEntity, albumUsers); }); } Future deleteAlbum(String albumId) async { return _db.transaction(() async { - await _db.remoteAlbumEntity.deleteWhere( - (table) => table.id.equals(albumId), - ); + await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId)); }); } Stream watchAlbum(String albumId) { - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId)) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId)) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); return query.map((row) { - final album = row.readTable(_db.remoteAlbumEntity).toDto( - ownerName: row.read(_db.userEntity.name)!, - ); + final album = row.readTable(_db.remoteAlbumEntity).toDto(ownerName: row.read(_db.userEntity.name)!); return album; }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 212b9b7f34..1ab62b3442 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -30,17 +30,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } SingleOrNullSelectable _assetSelectable(String id) { - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..limit(1); + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -57,17 +56,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } Stream watchAsset(String id) { - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..limit(1); + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -81,9 +79,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } final query = _db.remoteAssetEntity.select() - ..where( - (row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(), - ) + ..where((row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not()) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); return query.map((row) => row.toDto()).get(); @@ -102,24 +98,22 @@ class RemoteAssetRepository extends DriftDatabaseRepository { "asset", ); - final query = asset.selectOnly().join([ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), - useColumns: false, - ), - ]) - ..addColumns([ - _db.remoteExifEntity.city, - _db.remoteExifEntity.assetId, - ]) - ..where( - _db.remoteExifEntity.city.isNotNull() & - asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & - asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), - ) - ..groupBy([_db.remoteExifEntity.city]) - ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); + final query = + asset.selectOnly().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId]) + ..where( + _db.remoteExifEntity.city.isNotNull() & + asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & + asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), + ) + ..groupBy([_db.remoteExifEntity.city]) + ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); return query.map((row) { final assetId = row.read(_db.remoteExifEntity.assetId); @@ -185,10 +179,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { for (final id in ids) { batch.update( _db.remoteExifEntity, - RemoteExifEntityCompanion( - latitude: Value(location.latitude), - longitude: Value(location.longitude), - ), + RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)), where: (e) => e.assetId.equals(id), ); } @@ -205,23 +196,14 @@ class RemoteAssetRepository extends DriftDatabaseRepository { await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); await _db.batch((batch) { - final companion = StackEntityCompanion( - ownerId: Value(userId), - primaryAssetId: Value(stack.primaryAssetId), - ); + final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId)); - batch.insert( - _db.stackEntity, - companion.copyWith(id: Value(stack.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion)); for (final assetId in stack.assetIds) { batch.update( _db.remoteAssetEntity, - RemoteAssetEntityCompanion( - stackId: Value(stack.id), - ), + RemoteAssetEntityCompanion(stackId: Value(stack.id)), where: (e) => e.id.equals(assetId), ); } diff --git a/mobile/lib/infrastructure/repositories/search_api.repository.dart b/mobile/lib/infrastructure/repositories/search_api.repository.dart index 441219a02f..129746120b 100644 --- a/mobile/lib/infrastructure/repositories/search_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/search_api.repository.dart @@ -66,12 +66,5 @@ class SearchApiRepository extends ApiRepository { String? state, String? make, String? model, - }) => - _api.getSearchSuggestions( - type, - country: country, - state: state, - make: make, - model: model, - ); + }) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model); } diff --git a/mobile/lib/infrastructure/repositories/stack.repository.dart b/mobile/lib/infrastructure/repositories/stack.repository.dart index cdac5fe4ad..28f7496f97 100644 --- a/mobile/lib/infrastructure/repositories/stack.repository.dart +++ b/mobile/lib/infrastructure/repositories/stack.repository.dart @@ -18,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository { extension on StackEntityData { Stack toDto() { - return Stack( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ); + return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId); } } diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 990c2ad65d..6467767aa2 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -23,11 +23,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { .filter() .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .watch(fireImmediately: true) - .asyncExpand( - (entities) => Stream.fromFutures( - entities.map((e) async => _toUpdateEvent(e)), - ), - ); + .asyncExpand((entities) => Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e)))); } Future delete(StoreKey key) async { @@ -68,14 +64,17 @@ class IsarStoreRepository extends IsarDatabaseRepository { return StoreDto(key, value); } - Future _toValue(StoreKey key, StoreValue entity) async => switch (key.type) { - const (int) => entity.intValue, - const (String) => entity.strValue, - const (bool) => entity.intValue == 1, - const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), - const (UserDto) => entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), - _ => null, - } as T?; + Future _toValue(StoreKey key, StoreValue entity) async => + switch (key.type) { + const (int) => entity.intValue, + const (String) => entity.strValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => + entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), + _ => null, + } + as T?; Future _fromValue(StoreKey key, T value) async { final (int? intValue, String? strValue) = switch (key.type) { @@ -83,13 +82,8 @@ class IsarStoreRepository extends IsarDatabaseRepository { const (String) => (null, value as String), const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), - const (UserDto) => ( - null, - (await IsarUserRepository(_db).update(value as UserDto)).id, - ), - _ => throw UnsupportedError( - "Unsupported primitive type: ${key.type} for key: ${key.name}", - ), + const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id), + _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreValue(key.id, intValue: intValue, strValue: strValue); } diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 6727f19c64..78b2a9d957 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -27,10 +27,7 @@ class SyncApiRepository { final client = httpClient ?? http.Client(); final endpoint = "${_api.apiClient.basePath}/sync/stream"; - final headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/jsonlines+json', - }; + final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'}; final headerParams = {}; await _api.applyToParams([], headerParams); @@ -78,10 +75,7 @@ class SyncApiRepository { if (response.statusCode != 200) { final errorBody = await response.stream.bytesToString(); - throw ApiException( - response.statusCode, - 'Failed to get sync stream: $errorBody', - ); + throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody'); } await for (final chunk in response.stream.transform(utf8.decoder)) { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 54bc01cfa2..64b0661e16 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -42,16 +42,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final user in data) { - final companion = UserEntityCompanion( - name: Value(user.name), - email: Value(user.email), - ); + final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email)); - batch.insert( - _db.userEntity, - companion.copyWith(id: Value(user.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } }); } catch (error, stack) { @@ -66,10 +59,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final partner in data) { batch.delete( _db.partnerEntity, - PartnerEntityCompanion( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), ); } }); @@ -87,10 +77,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { batch.insert( _db.partnerEntity, - companion.copyWith( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), onConflict: DoUpdate((_) => companion), ); } @@ -101,24 +88,16 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.remoteAssetEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.assetId)), - ); + await _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId))); } catch (error, stack) { _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); rethrow; } } - Future updateAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final asset in data) { @@ -152,10 +131,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAssetsExifV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsExifV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final exif in data) { @@ -191,20 +167,14 @@ class SyncStreamRepository extends DriftDatabaseRepository { } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAssetsExifV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack); rethrow; } } Future deleteAlbumsV1(Iterable data) async { try { - await _db.remoteAlbumEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.albumId)), - ); + await _db.remoteAlbumEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId))); } catch (error, stack) { _logger.severe('Error: deleteAlbumsV1', error, stack); rethrow; @@ -245,10 +215,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final album in data) { batch.delete( _db.remoteAlbumUserEntity, - RemoteAlbumUserEntityCompanion( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)), ); } }); @@ -258,49 +225,32 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumUsersV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumUsersV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { - final companion = RemoteAlbumUserEntityCompanion( - role: Value(album.role.toAlbumUserRole()), - ); + final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole())); batch.insert( _db.remoteAlbumUserEntity, - companion.copyWith( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)), onConflict: DoUpdate((_) => companion), ); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumUsersV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack); rethrow; } } - Future deleteAlbumToAssetsV1( - Iterable data, - ) async { + Future deleteAlbumToAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final album in data) { batch.delete( _db.remoteAlbumAssetEntity, - RemoteAlbumAssetEntityCompanion( - albumId: Value(album.albumId), - assetId: Value(album.assetId), - ), + RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)), ); } }); @@ -310,10 +260,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumToAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumToAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { @@ -322,19 +269,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { assetId: Value(album.assetId), ); - batch.insert( - _db.remoteAlbumAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumToAssetsV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack); rethrow; } } @@ -371,9 +310,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { Future deleteMemoriesV1(Iterable data) async { try { - await _db.memoryEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.memoryId)), - ); + await _db.memoryEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.memoryId))); } catch (error, stack) { _logger.severe('Error: deleteMemoriesV1', error, stack); rethrow; @@ -384,16 +321,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final asset in data) { - final companion = MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ); + final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)); - batch.insert( - _db.memoryAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { @@ -402,18 +332,13 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteMemoryAssetsV1( - Iterable data, - ) async { + Future deleteMemoryAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final asset in data) { batch.delete( _db.memoryAssetEntity, - MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ), + MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)), ); } }); @@ -423,10 +348,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final stack in data) { @@ -450,36 +372,24 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.stackEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.stackId)), - ); + await _db.stackEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.stackId))); } catch (error, stack) { _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); rethrow; } } - Future updateUserMetadatasV1( - Iterable data, - ) async { + Future updateUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { - final companion = UserMetadataEntityCompanion( - value: Value(userMetadata.value as Map), - ); + final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map)); batch.insert( _db.userMetadataEntity, - companion.copyWith( - userId: Value(userMetadata.userId), - key: Value(userMetadata.key.toUserMetadataKey()), - ), + companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())), onConflict: DoUpdate((_) => companion), ); } @@ -490,9 +400,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteUserMetadatasV1( - Iterable data, - ) async { + Future deleteUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { @@ -540,16 +448,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deletePeopleV1( - Iterable data, - ) async { + Future deletePeopleV1(Iterable data) async { try { await _db.batch((batch) { for (final person in data) { - batch.deleteWhere( - _db.personEntity, - (row) => row.id.equals(person.personId), - ); + batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId)); } }); } catch (error, stack) { @@ -591,10 +494,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final assetFace in data) { - batch.deleteWhere( - _db.assetFaceEntity, - (row) => row.id.equals(assetFace.assetFaceId), - ); + batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId)); } }); } catch (error, stack) { @@ -606,54 +506,54 @@ class SyncStreamRepository extends DriftDatabaseRepository { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } extension on AssetOrder { AlbumAssetOrder toAlbumAssetOrder() => switch (this) { - AssetOrder.asc => AlbumAssetOrder.asc, - AssetOrder.desc => AlbumAssetOrder.desc, - _ => throw Exception('Unknown AssetOrder value: $this'), - }; + AssetOrder.asc => AlbumAssetOrder.asc, + AssetOrder.desc => AlbumAssetOrder.desc, + _ => throw Exception('Unknown AssetOrder value: $this'), + }; } extension on MemoryType { MemoryTypeEnum toMemoryType() => switch (this) { - MemoryType.onThisDay => MemoryTypeEnum.onThisDay, - _ => throw Exception('Unknown MemoryType value: $this'), - }; + MemoryType.onThisDay => MemoryTypeEnum.onThisDay, + _ => throw Exception('Unknown MemoryType value: $this'), + }; } extension on api.AlbumUserRole { AlbumUserRole toAlbumUserRole() => switch (this) { - api.AlbumUserRole.editor => AlbumUserRole.editor, - api.AlbumUserRole.viewer => AlbumUserRole.viewer, - _ => throw Exception('Unknown AlbumUserRole value: $this'), - }; + api.AlbumUserRole.editor => AlbumUserRole.editor, + api.AlbumUserRole.viewer => AlbumUserRole.viewer, + _ => throw Exception('Unknown AlbumUserRole value: $this'), + }; } extension on api.AssetVisibility { AssetVisibility toAssetVisibility() => switch (this) { - api.AssetVisibility.timeline => AssetVisibility.timeline, - api.AssetVisibility.hidden => AssetVisibility.hidden, - api.AssetVisibility.archive => AssetVisibility.archive, - api.AssetVisibility.locked => AssetVisibility.locked, - _ => throw Exception('Unknown AssetVisibility value: $this'), - }; + api.AssetVisibility.timeline => AssetVisibility.timeline, + api.AssetVisibility.hidden => AssetVisibility.hidden, + api.AssetVisibility.archive => AssetVisibility.archive, + api.AssetVisibility.locked => AssetVisibility.locked, + _ => throw Exception('Unknown AssetVisibility value: $this'), + }; } extension on api.UserMetadataKey { UserMetadataKey toUserMetadataKey() => switch (this) { - api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, - api.UserMetadataKey.preferences => UserMetadataKey.preferences, - api.UserMetadataKey.license => UserMetadataKey.license, - _ => throw Exception('Unknown UserMetadataKey value: $this'), - }; + api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, + api.UserMetadataKey.preferences => UserMetadataKey.preferences, + api.UserMetadataKey.license => UserMetadataKey.license, + _ => throw Exception('Unknown UserMetadataKey value: $this'), + }; } extension on String { diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 772fb74f84..a2c14a363f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -21,9 +21,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Stream> watchTimelineUserIds(String userId) { final query = _db.partnerEntity.selectOnly() ..addColumns([_db.partnerEntity.sharedById]) - ..where( - _db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId), - ); + ..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId)); return query .map((row) => row.read(_db.partnerEntity.sharedById)!) @@ -33,25 +31,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchMainBucket( - userIds, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getMainBucketAssets( - userIds, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy), + assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count), + ); - Stream> _watchMainBucket( - List userIds, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchMainBucket(List userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchMainBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket"); } return _db.mergedAssetDrift @@ -64,11 +50,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .throttle(const Duration(seconds: 3), trailing: true); } - Future> _getMainBucketAssets( - List userIds, { - required int offset, - required int count, - }) { + Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( @@ -109,21 +91,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchLocalAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getLocalAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchLocalAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.localAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -134,22 +106,23 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final assetCountExp = _db.localAssetEntity.id.count(); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.localAssetEntity.selectOnly().join([ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ]) - ..addColumns([assetCountExp, dateExp]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); + final query = + _db.localAssetEntity.selectOnly().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([assetCountExp, dateExp]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { final timeline = row.read(dateExp)!.dateFmt(groupBy); @@ -158,54 +131,37 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getLocalAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) { - final query = _db.localAssetEntity.select().join( - [ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ], - ) - ..addColumns([_db.remoteAssetEntity.id]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) { + final query = + _db.localAssetEntity.select().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteAssetEntity.id]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchRemoteAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getRemoteAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchRemoteAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.remoteAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -213,56 +169,53 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .watch() .map((results) => results.isNotEmpty ? results.first : []) .handleError((error) { - return []; - }); + return []; + }); } - return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).watch().switchMap((albums) { - if (albums.isEmpty) { - return Stream.value([]); - } + return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) + .watch() + .switchMap((albums) { + if (albums.isEmpty) { + return Stream.value([]); + } - final album = albums.first; - final isAscending = album.order == AlbumAssetOrder.asc; - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + final album = albums.first; + final isAscending = album.order == AlbumAssetOrder.asc; + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..join([ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ) - ..groupBy([dateExp]); + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]); - if (isAscending) { - query.orderBy([OrderingTerm.asc(dateExp)]); - } else { - query.orderBy([OrderingTerm.desc(dateExp)]); - } + if (isAscending) { + query.orderBy([OrderingTerm.asc(dateExp)]); + } else { + query.orderBy([OrderingTerm.desc(dateExp)]); + } - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - }).handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + }) + .handleError((error) { + // If there's an error (e.g., album was deleted), return empty buckets + return []; + }); } - Future> _getRemoteAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) async { + Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull(); // If album doesn't exist (was deleted), return empty list @@ -272,17 +225,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - )..where( - _db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ); + final query = _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); if (isAscending) { query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); @@ -296,69 +245,57 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery fromAssets(List assets) => ( - bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), - ); + bucketSource: () => Stream.value(_generateBuckets(assets.length)), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), + ); TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + groupBy: groupBy, + ); TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), - groupBy: groupBy, - joinLocal: true, - ); + filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + groupBy: groupBy, + joinLocal: true, + ); TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), + groupBy: groupBy, + ); TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.type.equalsValue(AssetType.video) & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery place(String place, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchPlaceBucket( - place, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getPlaceBucketAssets( - place, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy), + assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), + ); - Stream> _watchPlaceBucket( - String place, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchPlaceBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket"); } final assetCountExp = _db.remoteAssetEntity.id.count(); @@ -388,27 +325,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getPlaceBucketAssets( - String place, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - ) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & - _db.remoteExifEntity.city.equals(place), - ) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getPlaceBucketAssets(String place, {required int offset, required int count}) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteExifEntity.city.equals(place), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } @@ -419,12 +351,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), - assetSource: (offset, count) => _getRemoteAssets( - filter: filter, - offset: offset, - count: count, - joinLocal: joinLocal, - ), + assetSource: (offset, count) => + _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal), ); } @@ -460,17 +388,18 @@ class DriftTimelineRepository extends DriftDatabaseRepository { bool joinLocal = false, }) { if (joinLocal) { - final query = _db.remoteAssetEntity.select().join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..addColumns([_db.localAssetEntity.id]) - ..where(filter(_db.remoteAssetEntity)) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); + final query = + _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -507,9 +436,7 @@ extension on Expression { return switch (groupBy) { GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date, GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; } } @@ -519,9 +446,7 @@ extension on String { final format = switch (groupBy) { GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.month => "y-M", - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; try { return DateFormat(format).parse(this); diff --git a/mobile/lib/infrastructure/repositories/user_api.repository.dart b/mobile/lib/infrastructure/repositories/user_api.repository.dart index 0ee3deb4b1..d21a1b71a6 100644 --- a/mobile/lib/infrastructure/repositories/user_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_api.repository.dart @@ -17,15 +17,8 @@ class UserApiRepository extends ApiRepository { return UserConverter.fromAdminDto(adminDto, preferenceDto); } - Future createProfileImage({ - required String name, - required Uint8List data, - }) async { - final res = await checkNull( - _api.createProfileImage( - MultipartFile.fromBytes('file', data, filename: name), - ), - ); + Future createProfileImage({required String name, required Uint8List data}) async { + final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name))); return res.profileImagePath; } diff --git a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart index 81a4cd7945..7205c7f73a 100644 --- a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart @@ -18,20 +18,8 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { extension on UserMetadataEntityData { UserMetadata toDto() => switch (key) { - UserMetadataKey.onboarding => UserMetadata( - userId: userId, - key: key, - onboarding: Onboarding.fromMap(value), - ), - UserMetadataKey.preferences => UserMetadata( - userId: userId, - key: key, - preferences: Preferences.fromMap(value), - ), - UserMetadataKey.license => UserMetadata( - userId: userId, - key: key, - license: License.fromMap(value), - ), - }; + UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)), + UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)), + UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)), + }; } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index eb7b24737e..19958beabc 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -6,63 +6,59 @@ import 'package:openapi/api.dart'; abstract final class UserConverter { /// Base user dto used where the complete user object is not required static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + ); - static UserDto fromAdminDto( - UserAdminResponseDto adminDto, [ - UserPreferencesResponseDto? preferenceDto, - ]) => - UserDto( - id: adminDto.id, - email: adminDto.email, - name: adminDto.name, - isAdmin: adminDto.isAdmin, - updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, - avatarColor: adminDto.avatarColor.toAvatarColor(), - memoryEnabled: preferenceDto?.memories.enabled ?? true, - inTimeline: false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, - ); + static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto( + id: adminDto.id, + email: adminDto.email, + name: adminDto.name, + isAdmin: adminDto.isAdmin, + updatedAt: adminDto.updatedAt, + profileImagePath: adminDto.profileImagePath, + avatarColor: adminDto.avatarColor.toAvatarColor(), + memoryEnabled: preferenceDto?.memories.enabled ?? true, + inTimeline: false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, + quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - memoryEnabled: false, - inTimeline: dto.inTimeline ?? false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + memoryEnabled: false, + inTimeline: dto.inTimeline ?? false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: 0, + quotaSizeInBytes: 0, + ); } extension on UserAvatarColor { AvatarColor toAvatarColor() => switch (this) { - UserAvatarColor.red => AvatarColor.red, - UserAvatarColor.green => AvatarColor.green, - UserAvatarColor.blue => AvatarColor.blue, - UserAvatarColor.purple => AvatarColor.purple, - UserAvatarColor.orange => AvatarColor.orange, - UserAvatarColor.pink => AvatarColor.pink, - UserAvatarColor.amber => AvatarColor.amber, - UserAvatarColor.yellow => AvatarColor.yellow, - UserAvatarColor.gray => AvatarColor.gray, - UserAvatarColor.primary || _ => AvatarColor.primary, - }; + UserAvatarColor.red => AvatarColor.red, + UserAvatarColor.green => AvatarColor.green, + UserAvatarColor.blue => AvatarColor.blue, + UserAvatarColor.purple => AvatarColor.purple, + UserAvatarColor.orange => AvatarColor.orange, + UserAvatarColor.pink => AvatarColor.pink, + UserAvatarColor.amber => AvatarColor.amber, + UserAvatarColor.yellow => AvatarColor.yellow, + UserAvatarColor.gray => AvatarColor.gray, + UserAvatarColor.primary || _ => AvatarColor.primary, + }; } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index e52f298413..63220295ff 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -50,10 +50,7 @@ void main() async { runApp( ProviderScope( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], + overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)], child: const MainWidget(), ), ); @@ -100,23 +97,15 @@ Future initApp() async { globalConfig: (Config.holdingQueue, (1000, 1000, 1000)), ); - await FileDownloader().trackTasksInGroup( - kDownloadGroupLivePhoto, - markDownloadedComplete: false, - ); + await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); await FileDownloader().trackTasks(); - LicenseRegistry.addLicense( - () async* { - for (final license in nonPubLicenses.entries) { - yield LicenseEntryWithLineBreaks( - [license.key], - license.value, - ); - } - }, - ); + LicenseRegistry.addLicense(() async* { + for (final license in nonPubLicenses.entries) { + yield LicenseEntryWithLineBreaks([license.key], license.value); + } + }); } class ImmichApp extends ConsumerStatefulWidget { @@ -160,9 +149,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // Sets the navigation bar color - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - ); + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); if (Platform.isAndroid) { // Android 8 does not support transparent app bars final info = await DeviceInfoPlugin().androidInfo; @@ -177,40 +164,22 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve void _configureFileDownloaderNotifications() { FileDownloader().configureNotificationForGroup( kDownloadGroupImage, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), progressBar: true, ); FileDownloader().configureNotificationForGroup( kDownloadGroupVideo, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), progressBar: true, ); FileDownloader().configureNotificationForGroup( kManualUploadGroup, - running: TaskNotification( - 'uploading_media'.tr(), - '${'file_name'.tr()}: {displayName}', - ), - complete: TaskNotification( - 'upload_finished'.tr(), - '${'file_name'.tr()}: {displayName}', - ), + running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'), + complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'), progressBar: true, ); } @@ -222,19 +191,13 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name; if (deepLink.uri.scheme == "immich") { - final proposedRoute = await deepLinkHandler.handleScheme( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart); return proposedRoute; } if (deepLink.uri.host == "my.immich.app") { - final proposedRoute = await deepLinkHandler.handleMyImmichApp( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart); return proposedRoute; } @@ -275,9 +238,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve final immichTheme = ref.watch(immichThemeProvider); return ProviderScope( - overrides: [ - localeProvider.overrideWithValue(context.locale), - ], + overrides: [localeProvider.overrideWithValue(context.locale)], child: MaterialApp.router( title: 'Immich', debugShowCheckedModeBanner: true, @@ -285,14 +246,8 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve supportedLocales: context.supportedLocales, locale: context.locale, themeMode: ref.watch(immichThemeModeProvider), - darkTheme: getThemeData( - colorScheme: immichTheme.dark, - locale: context.locale, - ), - theme: getThemeData( - colorScheme: immichTheme.light, - locale: context.locale, - ), + darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale), + theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale), routerConfig: router.config( deepLinkBuilder: _deepLinkBuilder, navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()], diff --git a/mobile/lib/models/albums/album_add_asset_response.model.dart b/mobile/lib/models/albums/album_add_asset_response.model.dart index fbc8a4d560..38dd989af5 100644 --- a/mobile/lib/models/albums/album_add_asset_response.model.dart +++ b/mobile/lib/models/albums/album_add_asset_response.model.dart @@ -7,15 +7,9 @@ class AlbumAddAssetsResponse { List alreadyInAlbum; int successfullyAdded; - AlbumAddAssetsResponse({ - required this.alreadyInAlbum, - required this.successfullyAdded, - }); + AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded}); - AlbumAddAssetsResponse copyWith({ - List? alreadyInAlbum, - int? successfullyAdded, - }) { + AlbumAddAssetsResponse copyWith({List? alreadyInAlbum, int? successfullyAdded}) { return AlbumAddAssetsResponse( alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum, successfullyAdded: successfullyAdded ?? this.successfullyAdded, @@ -23,10 +17,7 @@ class AlbumAddAssetsResponse { } Map toMap() { - return { - 'alreadyInAlbum': alreadyInAlbum, - 'successfullyAdded': successfullyAdded, - }; + return {'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded}; } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/albums/album_search.model.dart b/mobile/lib/models/albums/album_search.model.dart index ac4eedbff1..4f69e7e2e1 100644 --- a/mobile/lib/models/albums/album_search.model.dart +++ b/mobile/lib/models/albums/album_search.model.dart @@ -1,5 +1 @@ -enum QuickFilterMode { - all, - sharedWithMe, - myAlbums, -} +enum QuickFilterMode { all, sharedWithMe, myAlbums } diff --git a/mobile/lib/models/albums/album_viewer_page_state.model.dart b/mobile/lib/models/albums/album_viewer_page_state.model.dart index 823226b4a4..70427899ae 100644 --- a/mobile/lib/models/albums/album_viewer_page_state.model.dart +++ b/mobile/lib/models/albums/album_viewer_page_state.model.dart @@ -11,11 +11,7 @@ class AlbumViewerPageState { required this.editDescriptionText, }); - AlbumViewerPageState copyWith({ - bool? isEditAlbum, - String? editTitleText, - String? editDescriptionText, - }) { + AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) { return AlbumViewerPageState( isEditAlbum: isEditAlbum ?? this.isEditAlbum, editTitleText: editTitleText ?? this.editTitleText, diff --git a/mobile/lib/models/albums/asset_selection_page_result.model.dart b/mobile/lib/models/albums/asset_selection_page_result.model.dart index d921ac63ce..cc750f397f 100644 --- a/mobile/lib/models/albums/asset_selection_page_result.model.dart +++ b/mobile/lib/models/albums/asset_selection_page_result.model.dart @@ -4,9 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class AssetSelectionPageResult { final Set selectedAssets; - const AssetSelectionPageResult({ - required this.selectedAssets, - }); + const AssetSelectionPageResult({required this.selectedAssets}); @override bool operator ==(Object other) { if (identical(this, other)) return true; diff --git a/mobile/lib/models/asset_selection_state.dart b/mobile/lib/models/asset_selection_state.dart index c022ec3a3a..aded3064ce 100644 --- a/mobile/lib/models/asset_selection_state.dart +++ b/mobile/lib/models/asset_selection_state.dart @@ -13,12 +13,7 @@ class AssetSelectionState { this.selectedCount = 0, }); - AssetSelectionState copyWith({ - bool? hasRemote, - bool? hasLocal, - bool? hasMerged, - int? selectedCount, - }) { + AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) { return AssetSelectionState( hasRemote: hasRemote ?? this.hasRemote, hasLocal: hasLocal ?? this.hasLocal, @@ -28,10 +23,10 @@ class AssetSelectionState { } AssetSelectionState.fromSelection(Set selection) - : hasLocal = selection.any((e) => e.storage == AssetState.local), - hasMerged = selection.any((e) => e.storage == AssetState.merged), - hasRemote = selection.any((e) => e.storage == AssetState.remote), - selectedCount = selection.length; + : hasLocal = selection.any((e) => e.storage == AssetState.local), + hasMerged = selection.any((e) => e.storage == AssetState.merged), + hasRemote = selection.any((e) => e.storage == AssetState.remote), + selectedCount = selection.length; @override String toString() => diff --git a/mobile/lib/models/auth/auxilary_endpoint.model.dart b/mobile/lib/models/auth/auxilary_endpoint.model.dart index e876097d61..c7f472e111 100644 --- a/mobile/lib/models/auth/auxilary_endpoint.model.dart +++ b/mobile/lib/models/auth/auxilary_endpoint.model.dart @@ -5,19 +5,10 @@ class AuxilaryEndpoint { final String url; final AuxCheckStatus status; - const AuxilaryEndpoint({ - required this.url, - required this.status, - }); + const AuxilaryEndpoint({required this.url, required this.status}); - AuxilaryEndpoint copyWith({ - String? url, - AuxCheckStatus? status, - }) { - return AuxilaryEndpoint( - url: url ?? this.url, - status: status ?? this.status, - ); + AuxilaryEndpoint copyWith({String? url, AuxCheckStatus? status}) { + return AuxilaryEndpoint(url: url ?? this.url, status: status ?? this.status); } @override @@ -34,10 +25,7 @@ class AuxilaryEndpoint { int get hashCode => url.hashCode ^ status.hashCode; Map toMap() { - return { - 'url': url, - 'status': status.toMap(), - }; + return {'url': url, 'status': status.toMap()}; } factory AuxilaryEndpoint.fromMap(Map map) { @@ -55,9 +43,7 @@ class AuxilaryEndpoint { class AuxCheckStatus { final String name; - const AuxCheckStatus({ - required this.name, - }); + const AuxCheckStatus({required this.name}); const AuxCheckStatus._(this.name); static const loading = AuxCheckStatus._('loading'); @@ -75,24 +61,16 @@ class AuxCheckStatus { @override int get hashCode => name.hashCode; - AuxCheckStatus copyWith({ - String? name, - }) { - return AuxCheckStatus( - name: name ?? this.name, - ); + AuxCheckStatus copyWith({String? name}) { + return AuxCheckStatus(name: name ?? this.name); } Map toMap() { - return { - 'name': name, - }; + return {'name': name}; } factory AuxCheckStatus.fromMap(Map map) { - return AuxCheckStatus( - name: map['name'] as String, - ); + return AuxCheckStatus(name: map['name'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/auth/biometric_status.model.dart b/mobile/lib/models/auth/biometric_status.model.dart index 223b283279..ad2b06be04 100644 --- a/mobile/lib/models/auth/biometric_status.model.dart +++ b/mobile/lib/models/auth/biometric_status.model.dart @@ -5,18 +5,12 @@ class BiometricStatus { final List availableBiometrics; final bool canAuthenticate; - const BiometricStatus({ - required this.availableBiometrics, - required this.canAuthenticate, - }); + const BiometricStatus({required this.availableBiometrics, required this.canAuthenticate}); @override String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; - BiometricStatus copyWith({ - List? availableBiometrics, - bool? canAuthenticate, - }) { + BiometricStatus copyWith({List? availableBiometrics, bool? canAuthenticate}) { return BiometricStatus( availableBiometrics: availableBiometrics ?? this.availableBiometrics, canAuthenticate: canAuthenticate ?? this.canAuthenticate, diff --git a/mobile/lib/models/backup/available_album.model.dart b/mobile/lib/models/backup/available_album.model.dart index 96a19cf60b..502d0b66be 100644 --- a/mobile/lib/models/backup/available_album.model.dart +++ b/mobile/lib/models/backup/available_album.model.dart @@ -4,17 +4,9 @@ class AvailableAlbum { final Album album; final int assetCount; final DateTime? lastBackup; - const AvailableAlbum({ - required this.album, - required this.assetCount, - this.lastBackup, - }); + const AvailableAlbum({required this.album, required this.assetCount, this.lastBackup}); - AvailableAlbum copyWith({ - Album? album, - int? assetCount, - DateTime? lastBackup, - }) { + AvailableAlbum copyWith({Album? album, int? assetCount, DateTime? lastBackup}) { return AvailableAlbum( album: album ?? this.album, assetCount: assetCount ?? this.assetCount, diff --git a/mobile/lib/models/backup/backup_state.model.dart b/mobile/lib/models/backup/backup_state.model.dart index 39554736dd..635d925c3f 100644 --- a/mobile/lib/models/backup/backup_state.model.dart +++ b/mobile/lib/models/backup/backup_state.model.dart @@ -148,10 +148,7 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals( - other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds, - ) && + collectionEquals(other.selectedAlbumsBackupAssetsIds, selectedAlbumsBackupAssetsIds) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart index ca49450a31..da1e104ba3 100644 --- a/mobile/lib/models/backup/success_upload_asset.model.dart +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -5,17 +5,9 @@ class SuccessUploadAsset { final String remoteAssetId; final bool isDuplicate; - const SuccessUploadAsset({ - required this.candidate, - required this.remoteAssetId, - required this.isDuplicate, - }); + const SuccessUploadAsset({required this.candidate, required this.remoteAssetId, required this.isDuplicate}); - SuccessUploadAsset copyWith({ - BackupCandidate? candidate, - String? remoteAssetId, - bool? isDuplicate, - }) { + SuccessUploadAsset copyWith({BackupCandidate? candidate, String? remoteAssetId, bool? isDuplicate}) { return SuccessUploadAsset( candidate: candidate ?? this.candidate, remoteAssetId: remoteAssetId ?? this.remoteAssetId, diff --git a/mobile/lib/models/download/download_state.model.dart b/mobile/lib/models/download/download_state.model.dart index b2bd389bc2..82d4e31253 100644 --- a/mobile/lib/models/download/download_state.model.dart +++ b/mobile/lib/models/download/download_state.model.dart @@ -10,17 +10,9 @@ class DownloadInfo { // enum final TaskStatus status; - const DownloadInfo({ - required this.fileName, - required this.progress, - required this.status, - }); + const DownloadInfo({required this.fileName, required this.progress, required this.status}); - DownloadInfo copyWith({ - String? fileName, - double? progress, - TaskStatus? status, - }) { + DownloadInfo copyWith({String? fileName, double? progress, TaskStatus? status}) { return DownloadInfo( fileName: fileName ?? this.fileName, progress: progress ?? this.progress, @@ -29,11 +21,7 @@ class DownloadInfo { } Map toMap() { - return { - 'fileName': fileName, - 'progress': progress, - 'status': status.index, - }; + return {'fileName': fileName, 'progress': progress, 'status': status.index}; } factory DownloadInfo.fromMap(Map map) { @@ -67,17 +55,9 @@ class DownloadState { final TaskStatus downloadStatus; final Map taskProgress; final bool showProgress; - const DownloadState({ - required this.downloadStatus, - required this.taskProgress, - required this.showProgress, - }); + const DownloadState({required this.downloadStatus, required this.taskProgress, required this.showProgress}); - DownloadState copyWith({ - TaskStatus? downloadStatus, - Map? taskProgress, - bool? showProgress, - }) { + DownloadState copyWith({TaskStatus? downloadStatus, Map? taskProgress, bool? showProgress}) { return DownloadState( downloadStatus: downloadStatus ?? this.downloadStatus, taskProgress: taskProgress ?? this.taskProgress, diff --git a/mobile/lib/models/download/livephotos_medatada.model.dart b/mobile/lib/models/download/livephotos_medatada.model.dart index 9c0c7ae4e9..f77a1514ac 100644 --- a/mobile/lib/models/download/livephotos_medatada.model.dart +++ b/mobile/lib/models/download/livephotos_medatada.model.dart @@ -1,43 +1,25 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; -enum LivePhotosPart { - video, - image, -} +enum LivePhotosPart { video, image } class LivePhotosMetadata { // enum LivePhotosPart part; String id; - LivePhotosMetadata({ - required this.part, - required this.id, - }); + LivePhotosMetadata({required this.part, required this.id}); - LivePhotosMetadata copyWith({ - LivePhotosPart? part, - String? id, - }) { - return LivePhotosMetadata( - part: part ?? this.part, - id: id ?? this.id, - ); + LivePhotosMetadata copyWith({LivePhotosPart? part, String? id}) { + return LivePhotosMetadata(part: part ?? this.part, id: id ?? this.id); } Map toMap() { - return { - 'part': part.index, - 'id': id, - }; + return {'part': part.index, 'id': id}; } factory LivePhotosMetadata.fromMap(Map map) { - return LivePhotosMetadata( - part: LivePhotosPart.values[map['part'] as int], - id: map['id'] as String, - ); + return LivePhotosMetadata(part: LivePhotosPart.values[map['part'] as int], id: map['id'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/folder/recursive_folder.model.dart b/mobile/lib/models/folder/recursive_folder.model.dart index 62ec670fed..33ac0f4cb4 100644 --- a/mobile/lib/models/folder/recursive_folder.model.dart +++ b/mobile/lib/models/folder/recursive_folder.model.dart @@ -3,9 +3,5 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; class RecursiveFolder extends RootFolder { final String name; - const RecursiveFolder({ - required this.name, - required super.path, - required super.subfolders, - }); + const RecursiveFolder({required this.name, required super.path, required super.subfolders}); } diff --git a/mobile/lib/models/folder/root_folder.model.dart b/mobile/lib/models/folder/root_folder.model.dart index 567093ecd5..d4b791b915 100644 --- a/mobile/lib/models/folder/root_folder.model.dart +++ b/mobile/lib/models/folder/root_folder.model.dart @@ -4,8 +4,5 @@ class RootFolder { final List subfolders; final String path; - const RootFolder({ - required this.subfolders, - required this.path, - }); + const RootFolder({required this.subfolders, required this.path}); } diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart index 781eae792f..0f425306ff 100644 --- a/mobile/lib/models/map/map_marker.model.dart +++ b/mobile/lib/models/map/map_marker.model.dart @@ -4,24 +4,13 @@ import 'package:openapi/api.dart'; class MapMarker { final LatLng latLng; final String assetRemoteId; - const MapMarker({ - required this.latLng, - required this.assetRemoteId, - }); + const MapMarker({required this.latLng, required this.assetRemoteId}); - MapMarker copyWith({ - LatLng? latLng, - String? assetRemoteId, - }) { - return MapMarker( - latLng: latLng ?? this.latLng, - assetRemoteId: assetRemoteId ?? this.assetRemoteId, - ); + MapMarker copyWith({LatLng? latLng, String? assetRemoteId}) { + return MapMarker(latLng: latLng ?? this.latLng, assetRemoteId: assetRemoteId ?? this.assetRemoteId); } - MapMarker.fromDto(MapMarkerResponseDto dto) - : latLng = LatLng(dto.lat, dto.lon), - assetRemoteId = dto.id; + MapMarker.fromDto(MapMarkerResponseDto dto) : latLng = LatLng(dto.lat, dto.lon), assetRemoteId = dto.id; @override String toString() => 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; diff --git a/mobile/lib/models/memories/memory.model.dart b/mobile/lib/models/memories/memory.model.dart index fb85d70b9e..8a9db5d51b 100644 --- a/mobile/lib/models/memories/memory.model.dart +++ b/mobile/lib/models/memories/memory.model.dart @@ -7,19 +7,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class Memory { final String title; final List assets; - const Memory({ - required this.title, - required this.assets, - }); + const Memory({required this.title, required this.assets}); - Memory copyWith({ - String? title, - List? assets, - }) { - return Memory( - title: title ?? this.title, - assets: assets ?? this.assets, - ); + Memory copyWith({String? title, List? assets}) { + return Memory(title: title ?? this.title, assets: assets ?? this.assets); } @override diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart index 7ecb5af45c..6e4a083876 100644 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ b/mobile/lib/models/search/search_curated_content.model.dart @@ -14,30 +14,14 @@ class SearchCuratedContent { /// The id to lookup the asset from the server final String id; - const SearchCuratedContent({ - required this.label, - required this.id, - this.subtitle, - }); + const SearchCuratedContent({required this.label, required this.id, this.subtitle}); - SearchCuratedContent copyWith({ - String? label, - String? subtitle, - String? id, - }) { - return SearchCuratedContent( - label: label ?? this.label, - subtitle: subtitle ?? this.subtitle, - id: id ?? this.id, - ); + SearchCuratedContent copyWith({String? label, String? subtitle, String? id}) { + return SearchCuratedContent(label: label ?? this.label, subtitle: subtitle ?? this.subtitle, id: id ?? this.id); } Map toMap() { - return { - 'label': label, - 'subtitle': subtitle, - 'id': id, - }; + return {'label': label, 'subtitle': subtitle, 'id': id}; } factory SearchCuratedContent.fromMap(Map map) { diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 1c2167faac..7f27b4d333 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -8,30 +8,14 @@ class SearchLocationFilter { String? country; String? state; String? city; - SearchLocationFilter({ - this.country, - this.state, - this.city, - }); + SearchLocationFilter({this.country, this.state, this.city}); - SearchLocationFilter copyWith({ - String? country, - String? state, - String? city, - }) { - return SearchLocationFilter( - country: country ?? this.country, - state: state ?? this.state, - city: city ?? this.city, - ); + SearchLocationFilter copyWith({String? country, String? state, String? city}) { + return SearchLocationFilter(country: country ?? this.country, state: state ?? this.state, city: city ?? this.city); } Map toMap() { - return { - 'country': country, - 'state': state, - 'city': city, - }; + return {'country': country, 'state': state, 'city': city}; } factory SearchLocationFilter.fromMap(Map map) { @@ -64,26 +48,14 @@ class SearchLocationFilter { class SearchCameraFilter { String? make; String? model; - SearchCameraFilter({ - this.make, - this.model, - }); + SearchCameraFilter({this.make, this.model}); - SearchCameraFilter copyWith({ - String? make, - String? model, - }) { - return SearchCameraFilter( - make: make ?? this.make, - model: model ?? this.model, - ); + SearchCameraFilter copyWith({String? make, String? model}) { + return SearchCameraFilter(make: make ?? this.make, model: model ?? this.model); } Map toMap() { - return { - 'make': make, - 'model': model, - }; + return {'make': make, 'model': model}; } factory SearchCameraFilter.fromMap(Map map) { @@ -115,19 +87,10 @@ class SearchCameraFilter { class SearchDateFilter { DateTime? takenBefore; DateTime? takenAfter; - SearchDateFilter({ - this.takenBefore, - this.takenAfter, - }); + SearchDateFilter({this.takenBefore, this.takenAfter}); - SearchDateFilter copyWith({ - DateTime? takenBefore, - DateTime? takenAfter, - }) { - return SearchDateFilter( - takenBefore: takenBefore ?? this.takenBefore, - takenAfter: takenAfter ?? this.takenAfter, - ); + SearchDateFilter copyWith({DateTime? takenBefore, DateTime? takenAfter}) { + return SearchDateFilter(takenBefore: takenBefore ?? this.takenBefore, takenAfter: takenAfter ?? this.takenAfter); } Map toMap() { @@ -167,17 +130,9 @@ class SearchDisplayFilters { bool isNotInAlbum = false; bool isArchive = false; bool isFavorite = false; - SearchDisplayFilters({ - required this.isNotInAlbum, - required this.isArchive, - required this.isFavorite, - }); + SearchDisplayFilters({required this.isNotInAlbum, required this.isArchive, required this.isFavorite}); - SearchDisplayFilters copyWith({ - bool? isNotInAlbum, - bool? isArchive, - bool? isFavorite, - }) { + SearchDisplayFilters copyWith({bool? isNotInAlbum, bool? isArchive, bool? isFavorite}) { return SearchDisplayFilters( isNotInAlbum: isNotInAlbum ?? this.isNotInAlbum, isArchive: isArchive ?? this.isArchive, @@ -186,11 +141,7 @@ class SearchDisplayFilters { } Map toMap() { - return { - 'isNotInAlbum': isNotInAlbum, - 'isArchive': isArchive, - 'isFavorite': isFavorite, - }; + return {'isNotInAlbum': isNotInAlbum, 'isArchive': isArchive, 'isFavorite': isFavorite}; } factory SearchDisplayFilters.fromMap(Map map) { diff --git a/mobile/lib/models/search/search_result.model.dart b/mobile/lib/models/search/search_result.model.dart index 458a9b4abc..02553869bf 100644 --- a/mobile/lib/models/search/search_result.model.dart +++ b/mobile/lib/models/search/search_result.model.dart @@ -6,19 +6,10 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/models/server_info/server_config.model.dart b/mobile/lib/models/server_info/server_config.model.dart index 88c27443c4..37b98afadb 100644 --- a/mobile/lib/models/server_info/server_config.model.dart +++ b/mobile/lib/models/server_info/server_config.model.dart @@ -15,11 +15,7 @@ class ServerConfig { required this.mapLightStyleUrl, }); - ServerConfig copyWith({ - int? trashDays, - String? oauthButtonText, - String? externalDomain, - }) { + ServerConfig copyWith({int? trashDays, String? oauthButtonText, String? externalDomain}) { return ServerConfig( trashDays: trashDays ?? this.trashDays, oauthButtonText: oauthButtonText ?? this.oauthButtonText, @@ -34,11 +30,11 @@ class ServerConfig { 'ServerConfig(trashDays: $trashDays, oauthButtonText: $oauthButtonText, externalDomain: $externalDomain)'; ServerConfig.fromDto(ServerConfigDto dto) - : trashDays = dto.trashDays, - oauthButtonText = dto.oauthButtonText, - externalDomain = dto.externalDomain, - mapDarkStyleUrl = dto.mapDarkStyleUrl, - mapLightStyleUrl = dto.mapLightStyleUrl; + : trashDays = dto.trashDays, + oauthButtonText = dto.oauthButtonText, + externalDomain = dto.externalDomain, + mapDarkStyleUrl = dto.mapDarkStyleUrl, + mapLightStyleUrl = dto.mapLightStyleUrl; @override bool operator ==(covariant ServerConfig other) { diff --git a/mobile/lib/models/server_info/server_disk_info.model.dart b/mobile/lib/models/server_info/server_disk_info.model.dart index 8248097ca5..01042b9f6d 100644 --- a/mobile/lib/models/server_info/server_disk_info.model.dart +++ b/mobile/lib/models/server_info/server_disk_info.model.dart @@ -13,12 +13,7 @@ class ServerDiskInfo { required this.diskUsagePercentage, }); - ServerDiskInfo copyWith({ - String? diskAvailable, - String? diskSize, - String? diskUse, - double? diskUsagePercentage, - }) { + ServerDiskInfo copyWith({String? diskAvailable, String? diskSize, String? diskUse, double? diskUsagePercentage}) { return ServerDiskInfo( diskAvailable: diskAvailable ?? this.diskAvailable, diskSize: diskSize ?? this.diskSize, @@ -33,10 +28,10 @@ class ServerDiskInfo { } ServerDiskInfo.fromDto(ServerStorageResponseDto dto) - : diskAvailable = dto.diskAvailable, - diskSize = dto.diskSize, - diskUse = dto.diskUse, - diskUsagePercentage = dto.diskUsagePercentage; + : diskAvailable = dto.diskAvailable, + diskSize = dto.diskSize, + diskUse = dto.diskUse, + diskUsagePercentage = dto.diskUsagePercentage; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/server_info/server_features.model.dart b/mobile/lib/models/server_info/server_features.model.dart index 7e537ebf34..20b9f29619 100644 --- a/mobile/lib/models/server_info/server_features.model.dart +++ b/mobile/lib/models/server_info/server_features.model.dart @@ -13,12 +13,7 @@ class ServerFeatures { required this.passwordLogin, }); - ServerFeatures copyWith({ - bool? trash, - bool? map, - bool? oauthEnabled, - bool? passwordLogin, - }) { + ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin}) { return ServerFeatures( trash: trash ?? this.trash, map: map ?? this.map, @@ -33,10 +28,10 @@ class ServerFeatures { } ServerFeatures.fromDto(ServerFeaturesDto dto) - : trash = dto.trash, - map = dto.map, - oauthEnabled = dto.oauth, - passwordLogin = dto.passwordLogin; + : trash = dto.trash, + map = dto.map, + oauthEnabled = dto.oauth, + passwordLogin = dto.passwordLogin; @override bool operator ==(covariant ServerFeatures other) { diff --git a/mobile/lib/models/server_info/server_version.model.dart b/mobile/lib/models/server_info/server_version.model.dart index bd71536622..2cb41b0415 100644 --- a/mobile/lib/models/server_info/server_version.model.dart +++ b/mobile/lib/models/server_info/server_version.model.dart @@ -5,22 +5,10 @@ class ServerVersion { final int minor; final int patch; - const ServerVersion({ - required this.major, - required this.minor, - required this.patch, - }); + const ServerVersion({required this.major, required this.minor, required this.patch}); - ServerVersion copyWith({ - int? major, - int? minor, - int? patch, - }) { - return ServerVersion( - major: major ?? this.major, - minor: minor ?? this.minor, - patch: patch ?? this.patch, - ); + ServerVersion copyWith({int? major, int? minor, int? patch}) { + return ServerVersion(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch); } @override @@ -28,10 +16,7 @@ class ServerVersion { return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; } - ServerVersion.fromDto(ServerVersionResponseDto dto) - : major = dto.major, - minor = dto.minor, - patch = dto.patch_; + ServerVersion.fromDto(ServerVersionResponseDto dto) : major = dto.major, minor = dto.minor, patch = dto.patch_; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/shared_link/shared_link.model.dart b/mobile/lib/models/shared_link/shared_link.model.dart index 135d191b20..57a1f441eb 100644 --- a/mobile/lib/models/shared_link/shared_link.model.dart +++ b/mobile/lib/models/shared_link/shared_link.model.dart @@ -58,23 +58,23 @@ class SharedLink { } SharedLink.fromDto(SharedLinkResponseDto dto) - : id = dto.id, - allowDownload = dto.allowDownload, - allowUpload = dto.allowUpload, - description = dto.description, - password = dto.password, - expiresAt = dto.expiresAt, - key = dto.key, - showMetadata = dto.showMetadata, - type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, - title = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" - : "INDIVIDUAL SHARE", - thumbAssetId = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumThumbnailAssetId - : dto.assets.isNotEmpty - ? dto.assets[0].id - : null; + : id = dto.id, + allowDownload = dto.allowDownload, + allowUpload = dto.allowUpload, + description = dto.description, + password = dto.password, + expiresAt = dto.expiresAt, + key = dto.key, + showMetadata = dto.showMetadata, + type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, + title = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" + : "INDIVIDUAL SHARE", + thumbAssetId = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumThumbnailAssetId + : dto.assets.isNotEmpty + ? dto.assets[0].id + : null; @override String toString() => diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 61157f3674..ae05e4c492 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -5,21 +5,9 @@ import 'dart:io'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart'; -enum ShareIntentAttachmentType { - image, - video, -} +enum ShareIntentAttachmentType { image, video } -enum UploadStatus { - enqueued, - running, - complete, - notFound, - failed, - canceled, - waitingToRetry, - paused, -} +enum UploadStatus { enqueued, running, complete, notFound, failed, canceled, waitingToRetry, paused } class ShareIntentAttachment { final String path; @@ -90,9 +78,8 @@ class ShareIntentAttachment { String toJson() => json.encode(toMap()); - factory ShareIntentAttachment.fromJson(String source) => ShareIntentAttachment.fromMap( - json.decode(source) as Map, - ); + factory ShareIntentAttachment.fromJson(String source) => + ShareIntentAttachment.fromMap(json.decode(source) as Map); @override String toString() { diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart index c0fc8c9364..f40ac9ccae 100644 --- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart @@ -14,10 +14,7 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { final Album album; - const AlbumAdditionalSharedUserSelectionPage({ - super.key, - required this.album, - }); + const AlbumAdditionalSharedUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,17 +27,9 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -53,31 +42,19 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -87,31 +64,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -124,9 +85,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -138,19 +97,14 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { actions: [ TextButton( onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { for (var sharedUsers in album.sharedUsers) { - users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == album.ownerId, - ); + users.removeWhere((u) => u.id == sharedUsers.id || u.id == album.ownerId); } return buildUserList(users); diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index e72ecb0712..ccc4c44d43 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -13,11 +13,7 @@ import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { - const AlbumAssetSelectionPage({ - super.key, - required this.existingAssets, - this.canDeselect = false, - }); + const AlbumAssetSelectionPage({super.key, required this.existingAssets, this.canDeselect = false}); final Set existingAssets; final bool canDeselect; @@ -52,10 +48,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, ), title: selected.value.isEmpty - ? const Text( - 'add_photos', - style: TextStyle(fontSize: 18), - ).tr() + ? const Text('add_photos', style: TextStyle(fontSize: 18)).tr() : const Text( 'share_assets_selected', style: TextStyle(fontSize: 18), @@ -70,17 +63,12 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, child: Text( canDeselect ? "done" : "add", - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), ), ], ), - body: assetSelectionRenderList.widgetWhen( - onData: (data) => buildBody(data), - ), + body: assetSelectionRenderList.widgetWhen(onData: (data) => buildBody(data)), ); } } diff --git a/mobile/lib/pages/album/album_control_button.dart b/mobile/lib/pages/album/album_control_button.dart index c453ace618..578eb839a0 100644 --- a/mobile/lib/pages/album/album_control_button.dart +++ b/mobile/lib/pages/album/album_control_button.dart @@ -7,11 +7,7 @@ class AlbumControlButton extends ConsumerWidget { final void Function()? onAddPhotosPressed; final void Function()? onAddUsersPressed; - const AlbumControlButton({ - super.key, - this.onAddPhotosPressed, - this.onAddUsersPressed, - }); + const AlbumControlButton({super.key, this.onAddPhotosPressed, this.onAddUsersPressed}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/pages/album/album_date_range.dart b/mobile/lib/pages/album/album_date_range.dart index 57d808f226..dbfd9214f1 100644 --- a/mobile/lib/pages/album/album_date_range.dart +++ b/mobile/lib/pages/album/album_date_range.dart @@ -33,9 +33,7 @@ class AlbumDateRange extends ConsumerWidget { padding: const EdgeInsets.only(left: 16.0), child: Text( _getDateRangeText(startDate, endDate), - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceVariant, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant), ), ); } @@ -46,8 +44,9 @@ class AlbumDateRange extends ConsumerWidget { return DateFormat.yMMMd().format(startDate); } - final String startDateText = - (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format(startDate); + final String startDateText = (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format( + startDate, + ); final String endDateText = DateFormat.yMMMd().format(endDate); return "$startDateText - $endDateText"; } diff --git a/mobile/lib/pages/album/album_description.dart b/mobile/lib/pages/album/album_description.dart index 37c5beb2c2..383367e8b7 100644 --- a/mobile/lib/pages/album/album_description.dart +++ b/mobile/lib/pages/album/album_description.dart @@ -36,10 +36,7 @@ class AlbumDescription extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumDescription ?? 'add_a_description'.tr(), - style: context.textTheme.bodyLarge, - ), + child: Text(albumDescription ?? 'add_a_description'.tr(), style: context.textTheme.bodyLarge), ); } } diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index 4c51093345..e659340f26 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -51,9 +51,7 @@ class AlbumOptionsPage extends HookConsumerWidget { final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo( - const TabControllerRoute(children: [AlbumsRoute()]), - ); + context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); } else { showErrorMessage(); } @@ -110,10 +108,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return SafeArea( child: Padding( padding: const EdgeInsets.only(top: 24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [...actions], - ), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), ), ); }, @@ -123,20 +118,9 @@ class AlbumOptionsPage extends HookConsumerWidget { buildOwnerInfo() { return ListTile( leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(), - title: Text( - album.owner.value?.name ?? "", - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - album.owner.value?.email ?? "", - style: TextStyle(color: context.colorScheme.onSurfaceSecondary), - ), - trailing: Text( - "owner", - style: context.textTheme.labelLarge, - ).tr(), + title: Text(album.owner.value?.name ?? "", style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(album.owner.value?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).tr(), ); } @@ -148,22 +132,9 @@ class AlbumOptionsPage extends HookConsumerWidget { itemBuilder: (context, index) { final user = sharedUsers.value[index]; return ListTile( - leading: UserCircleAvatar( - user: user, - radius: 22, - ), - title: Text( - user.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - user.email, - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - ), - ), + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, ); @@ -206,9 +177,7 @@ class AlbumOptionsPage extends HookConsumerWidget { ).tr(), subtitle: Text( "let_others_respond", - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), ), buildSectionTitle("shared_album_section_people_title".tr()), diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart index 723bb1e252..fe1823ec61 100644 --- a/mobile/lib/pages/album/album_shared_user_icons.dart +++ b/mobile/lib/pages/album/album_shared_user_icons.dart @@ -41,11 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), - child: UserCircleAvatar( - user: sharedUsers.value[index], - radius: 18, - size: 36, - ), + child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36), ); }), itemCount: sharedUsers.value.length, diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index d5d963b206..562f02a2ab 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -25,10 +25,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { final suggestedShareUsers = ref.watch(otherUsersProvider); createSharedAlbum() async { - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.watch(albumTitleProvider), - assets, - ); + var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(ref.watch(albumTitleProvider), assets); if (newAlbum != null) { ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); @@ -40,9 +37,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { child: SnackBar( content: Text( 'select_user_for_sharing_page_err_album', - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), ), ); @@ -50,17 +45,9 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -75,11 +62,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { backgroundColor: context.primaryColor.withValues(alpha: 0.15), label: Text( user.email, - style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold), ), ), ), @@ -87,18 +70,12 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: const Text( 'suggestions', - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), ListView.builder( @@ -107,25 +84,14 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: buildTileIcon(users[index]), - title: Text( - users[index].email, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(users[index].email, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -138,10 +104,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - 'invite_to_album', - style: TextStyle(color: context.primaryColor), - ).tr(), + title: Text('invite_to_album', style: TextStyle(color: context.primaryColor)).tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -152,9 +115,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - style: TextButton.styleFrom( - foregroundColor: context.primaryColor, - ), + style: TextButton.styleFrom(foregroundColor: context.primaryColor), onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum, child: const Text( "create_album", diff --git a/mobile/lib/pages/album/album_title.dart b/mobile/lib/pages/album/album_title.dart index ccea200f3a..6c7fc3faaa 100644 --- a/mobile/lib/pages/album/album_title.dart +++ b/mobile/lib/pages/album/album_title.dart @@ -19,32 +19,20 @@ class AlbumTitle extends ConsumerWidget { return const (false, false, ''); } - return ( - album.ownerId == userId, - album.isRemote, - album.name, - ); + return (album.ownerId == userId, album.isRemote, album.name); }), ); if (isOwner && isRemote) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8), - child: AlbumViewerEditableTitle( - albumName: albumName, - titleFocusNode: titleFocusNode, - ), + child: AlbumViewerEditableTitle(albumName: albumName, titleFocusNode: titleFocusNode), ); } return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumName, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), + child: Text(albumName, style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700)), ); } } diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index 6d9519f4ed..97853fb96a 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -65,10 +65,7 @@ class AlbumViewer extends HookConsumerWidget { /// If they exist, add to selected asset state to show they are already selected. void onAddPhotosPressed() async { AssetSelectionPageResult? returnPayload = await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: album.assets, - canDeselect: false, - ), + AlbumAssetSelectionRoute(existingAssets: album.assets, canDeselect: false), ); if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) { @@ -98,9 +95,7 @@ class AlbumViewer extends HookConsumerWidget { onActivitiesPressed() { if (album.remoteId != null) { ref.read(currentAssetProvider.notifier).set(null); - context.pushRoute( - const ActivitiesRoute(), - ); + context.pushRoute(const ActivitiesRoute()); } } @@ -129,14 +124,8 @@ class AlbumViewer extends HookConsumerWidget { children: [ const SizedBox(height: 32), const AlbumDateRange(), - AlbumTitle( - key: const ValueKey("albumTitle"), - titleFocusNode: titleFocusNode, - ), - AlbumDescription( - key: const ValueKey("albumDescription"), - descriptionFocusNode: descriptionFocusNode, - ), + AlbumTitle(key: const ValueKey("albumTitle"), titleFocusNode: titleFocusNode), + AlbumDescription(key: const ValueKey("albumDescription"), descriptionFocusNode: descriptionFocusNode), const AlbumSharedUserIcons(), if (album.isRemote) Padding( diff --git a/mobile/lib/pages/album/album_viewer.page.dart b/mobile/lib/pages/album/album_viewer.page.dart index 146a93a0a6..c99dacd9b7 100644 --- a/mobile/lib/pages/album/album_viewer.page.dart +++ b/mobile/lib/pages/album/album_viewer.page.dart @@ -21,9 +21,7 @@ class AlbumViewerPage extends HookConsumerWidget { ref.listen(assetSelectionTimelineProvider, (_, __) {}); ref.listen(albumWatcher(albumId), (_, albumFuture) { - albumFuture.whenData( - (value) => ref.read(currentAlbumProvider.notifier).set(value), - ); + albumFuture.whenData((value) => ref.read(currentAlbumProvider.notifier).set(value)); }); return const Scaffold(body: AlbumViewer()); diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index aea4cfa2b8..5f155c2f0d 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -52,21 +52,18 @@ class AlbumsPage extends HookConsumerWidget { filterMode.value = mode; } - useEffect( - () { - searchController.addListener(() { + useEffect(() { + searchController.addListener(() { + onSearch(searchController.text, filterMode.value); + }); + + return () { + searchController.removeListener(() { onSearch(searchController.text, filterMode.value); }); - - return () { - searchController.removeListener(() { - onSearch(searchController.text, filterMode.value); - }); - debounceTimer.value?.cancel(); - }; - }, - [], - ); + debounceTimer.value?.cancel(); + }; + }, []); clearSearch() { filterMode.value = QuickFilterMode.all; @@ -79,13 +76,8 @@ class AlbumsPage extends HookConsumerWidget { showUploadButton: false, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - CreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(CreateAlbumRoute()), ), ], ), @@ -100,13 +92,8 @@ class AlbumsPage extends HookConsumerWidget { children: [ Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -124,10 +111,7 @@ class AlbumsPage extends HookConsumerWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: clearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: clearSearch) : null, controller: searchController, onChanged: (_) => onSearch(searchController.text, filterMode.value), @@ -153,10 +137,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.sharedWithMe, onTap: () { changeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), QuickFilterButton( @@ -164,10 +145,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.myAlbums, onTap: () { changeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -177,10 +155,7 @@ class AlbumsPage extends HookConsumerWidget { children: [ const SortButton(), IconButton( - icon: Icon( - isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: toggleViewMode, ), ], @@ -201,9 +176,7 @@ class AlbumsPage extends HookConsumerWidget { itemBuilder: (context, index) { return AlbumThumbnailCard( album: sorted[index], - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), showOwner: true, ); }, @@ -221,44 +194,22 @@ class AlbumsPage extends HookConsumerWidget { sorted[index].name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: sorted[index].ownerId != null ? Text( - '${'items_count'.t( - context: context, - args: { - 'count': sorted[index].assetCount, - }, - )} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': sorted[index].ownerName!, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': sorted[index].assetCount})} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': sorted[index].ownerName!}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, style: context.textTheme.bodyMedium?.copyWith( color: context.colorScheme.onSurfaceSecondary, ), ) : null, - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: ImmichThumbnail( - asset: sorted[index].thumbnail.value, - width: 80, - height: 80, - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: ImmichThumbnail(asset: sorted[index].thumbnail.value, width: 80, height: 80), ), // minVerticalPadding: 1, ), @@ -275,12 +226,7 @@ class AlbumsPage extends HookConsumerWidget { } class QuickFilterButton extends StatelessWidget { - const QuickFilterButton({ - super.key, - required this.isSelected, - required this.onTap, - required this.label, - }); + const QuickFilterButton({super.key, required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -291,18 +237,11 @@ class QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), @@ -329,15 +268,9 @@ class SortButton extends ConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: AlbumSortMode.values @@ -345,16 +278,18 @@ class SortButton extends ConsumerWidget { (mode) => MenuItemButton( leadingIcon: albumSortOption == mode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: - albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: - albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () { final selected = albumSortOption == mode; @@ -366,18 +301,12 @@ class SortButton extends ConsumerWidget { } }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( albumSortOption == mode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( diff --git a/mobile/lib/pages/backup/album_preview.page.dart b/mobile/lib/pages/backup/album_preview.page.dart index 4cdc180973..def31afcd4 100644 --- a/mobile/lib/pages/backup/album_preview.page.dart +++ b/mobile/lib/pages/backup/album_preview.page.dart @@ -22,23 +22,17 @@ class AlbumPreviewPage extends HookConsumerWidget { assets.value = await ref.read(albumMediaRepositoryProvider).getAssets(album.localId!); } - useEffect( - () { - getAssetsInAlbum(); - return null; - }, - [], - ); + useEffect(() { + getAssetsInAlbum(); + return null; + }, []); return Scaffold( appBar: AppBar( elevation: 0, title: Column( children: [ - Text( - album.name, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( @@ -52,10 +46,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), ], ), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_new_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_new_rounded)), ), body: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -65,11 +56,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), itemCount: assets.value.length, itemBuilder: (context, index) { - return ImmichThumbnail( - asset: assets.value[index], - width: 100, - height: 100, - ); + return ImmichThumbnail(asset: assets.value[index], width: 100, height: 100); }, ), ); diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index 69e118cb78..d222211577 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -23,45 +23,29 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { final isDarkTheme = context.isDarkTheme; final albums = ref.watch(backupProvider).availableAlbums; - useEffect( - () { - ref.watch(backupProvider.notifier).getBackupInfo(); - return null; - }, - [], - ); + useEffect(() { + ref.watch(backupProvider.notifier).getBackupInfo(); + return null; + }, []); buildAlbumSelectionList() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return AlbumInfoListTile( - album: albums[index], - ); - }), - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return AlbumInfoListTile(album: albums[index]); + }), childCount: albums.length), ), ); } buildAlbumSelectionGrid() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -74,9 +58,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), itemCount: albums.length, itemBuilder: ((context, index) { - return AlbumInfoCard( - album: albums[index], - ); + return AlbumInfoCard(album: albums[index]); }), ), ); @@ -101,10 +83,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -125,18 +104,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -155,13 +127,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: const Text( - "backup_album_selection_page_select_albums", - ).tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + title: const Text("backup_album_selection_page_select_albums").tr(), elevation: 0, ), body: CustomScrollView( @@ -172,25 +139,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).tr(), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text("backup_album_selection_page_selection_info", style: context.textTheme.titleSmall).tr(), ), - // Selected Album Chips + // Selected Album Chips Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - ...buildSelectedAlbumNameChip(), - ...buildExcludedAlbumNameChip(), - ], - ), + child: Wrap(children: [...buildSelectedAlbumNameChip(), ...buildExcludedAlbumNameChip()]), ), SettingsSwitchListTile( @@ -198,21 +154,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: "sync_albums".tr(), subtitle: "sync_upload_album_setting_subtitle".tr(), contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), + titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), + subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), onChanged: handleSyncAlbumToggle, ), ListTile( title: Text( "backup_album_selection_page_albums_device".tr( - namedArgs: { - 'count': ref.watch(backupProvider).availableAlbums.length.toString(), - }, + namedArgs: {'count': ref.watch(backupProvider).availableAlbums.length.toString()}, ), style: context.textTheme.titleSmall, ), @@ -220,46 +170,30 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), trailing: IconButton( splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), + icon: Icon(Icons.info, size: 20, color: context.primaryColor), onPressed: () { // show the dialog showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), content: SingleChildScrollView( child: ListBody( children: [ const Text( 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), + style: TextStyle(fontSize: 14), ).tr(), ], ), diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index 76a772884e..093ff952ae 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -31,52 +31,44 @@ class BackupControllerPage extends HookConsumerWidget { final didGetBackupInfo = useState(false); bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground; - bool shouldBackup = backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || + bool shouldBackup = + backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || !hasExclusiveAccess ? false : true; - useEffect( - () { - // Update the background settings information just to make sure we - // have the latest, since the platform channel will not update - // automatically - if (Platform.isIOS) { - ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); - } + useEffect(() { + // Update the background settings information just to make sure we + // have the latest, since the platform channel will not update + // automatically + if (Platform.isIOS) { + ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); + } - ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); + ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); - return () { - WakelockPlus.disable(); - }; - }, - [], - ); + return () { + WakelockPlus.disable(); + }; + }, []); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { - ref.watch(backupProvider.notifier).getBackupInfo(); - didGetBackupInfo.value = true; - } - return null; - }, - [backupState.backupProgress], - ); + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { + ref.watch(backupProvider.notifier).getBackupInfo(); + didGetBackupInfo.value = true; + } + return null; + }, [backupState.backupProgress]); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - WakelockPlus.enable(); - } else { - WakelockPlus.disable(); - } + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.inProgress) { + WakelockPlus.enable(); + } else { + WakelockPlus.disable(); + } - return null; - }, - [backupState.backupProgress], - ); + return null; + }, [backupState.backupProgress]); Widget buildSelectedAlbumName() { var text = "backup_controller_page_backup_selected".tr(); @@ -95,9 +87,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -105,9 +95,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -126,9 +114,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -141,22 +127,14 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Card( shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -164,9 +142,7 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -181,12 +157,7 @@ class BackupControllerPage extends HookConsumerWidget { // waited until backup albums are stored in DB ref.read(albumProvider.notifier).refreshDeviceAlbums(); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -202,11 +173,10 @@ class BackupControllerPage extends HookConsumerWidget { Widget buildBackupButton() { return Padding( - padding: const EdgeInsets.only( - top: 24, - ), + padding: const EdgeInsets.only(top: 24), child: Container( - child: backupState.backupProgress == BackUpProgressEnum.inProgress || + child: + backupState.backupProgress == BackUpProgressEnum.inProgress || backupState.backupProgress == BackUpProgressEnum.manualInProgress ? ElevatedButton( style: ElevatedButton.styleFrom( @@ -221,22 +191,13 @@ class BackupControllerPage extends HookConsumerWidget { ref.read(backupProvider.notifier).cancelBackup(); } }, - child: const Text( - "cancel", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("cancel", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ) : ElevatedButton( onPressed: shouldBackup ? startBackup : null, child: const Text( "backup_controller_page_start_backup", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ).tr(), ), ), @@ -246,36 +207,28 @@ class BackupControllerPage extends HookConsumerWidget { buildBackgroundBackupInfo() { return const ListTile( leading: Icon(Icons.info_outline_rounded), - title: Text( - "Background backup is currently running, cannot start manual backup", - ), + title: Text("Background backup is currently running, cannot start manual backup"), ); } buildLoadingIndicator() { return const Padding( padding: EdgeInsets.only(top: 42.0), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( elevation: 0, - title: const Text( - "backup_controller_page_backup", - ).tr(), + title: const Text("backup_controller_page_backup").tr(), leading: IconButton( onPressed: () { ref.watch(websocketProvider.notifier).listenUploadEvent(); context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), actions: [ Padding( @@ -283,9 +236,7 @@ class BackupControllerPage extends HookConsumerWidget { child: IconButton( onPressed: () => context.pushRoute(const BackupOptionsRoute()), splashRadius: 24, - icon: const Icon( - Icons.settings_outlined, - ), + icon: const Icon(Icons.settings_outlined), ), ), ], @@ -325,10 +276,7 @@ class BackupControllerPage extends HookConsumerWidget { if (!hasExclusiveAccess) buildBackgroundBackupInfo(), buildBackupButton(), ] - : [ - buildFolderSelectionTile(), - if (!didGetBackupInfo.value) buildLoadingIndicator(), - ], + : [buildFolderSelectionTile(), if (!didGetBackupInfo.value) buildLoadingIndicator()], ), ), ], diff --git a/mobile/lib/pages/backup/backup_options.page.dart b/mobile/lib/pages/backup/backup_options.page.dart index 29822cab15..846a32a742 100644 --- a/mobile/lib/pages/backup/backup_options.page.dart +++ b/mobile/lib/pages/backup/backup_options.page.dart @@ -15,9 +15,7 @@ class BackupOptionsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BackupSettings(), diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index f691eb576b..6d31c75946 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -51,35 +51,25 @@ class _DriftBackupPageState extends ConsumerState { Widget build(BuildContext context) { final selectedAlbum = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); return Scaffold( appBar: AppBar( elevation: 0, - title: Text( - "backup_controller_page_backup".t(), - ), + title: Text("backup_controller_page_backup".t()), leading: IconButton( onPressed: () { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: Stack( children: [ Padding( - padding: const EdgeInsets.only( - left: 16.0, - right: 16, - bottom: 32, - ), + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), child: ListView( children: [ const SizedBox(height: 8), @@ -89,15 +79,10 @@ class _DriftBackupPageState extends ConsumerState { const _BackupCard(), const _RemainderCard(), const Divider(), - BackupToggleButton( - onStart: () async => await startBackup(), - onStop: () async => await stopBackup(), - ), + BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()), TextButton.icon( icon: const Icon(Icons.info_outline_rounded), - onPressed: () => context.pushRoute( - const DriftUploadDetailRoute(), - ), + onPressed: () => context.pushRoute(const DriftUploadDetailRoute()), label: Text("view_details".t(context: context)), ), ], @@ -119,9 +104,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_backup_selected".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); if (albums.isNotEmpty) { @@ -137,9 +120,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -147,9 +128,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -159,9 +138,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_excluded".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.excluded, - ) + .where((album) => album.backupSelection == BackupSelection.excluded) .toList(); if (albums.isNotEmpty) { @@ -173,9 +150,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -186,19 +161,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { return Card( shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(20)), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -206,9 +175,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -224,12 +191,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { } ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ); diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 3f12328a06..396b711d07 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -119,9 +119,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState setState(() => _searchQuery = value.trim()), ) - : const Text( - "backup_album_selection_page_select_albums", - ).t(context: context), + : const Text("backup_album_selection_page_select_albums").t(context: context), actions: [ if (!_isSearchMode) IconButton( @@ -151,27 +149,20 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState 600) { - return _AlbumSelectionGrid( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); + return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); } else { - return _AlbumSelectionList( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); + return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); } }, ), @@ -285,10 +250,7 @@ class _AlbumSelectionList extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionList({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -304,24 +266,15 @@ class _AlbumSelectionList extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); - }), - childCount: filteredAlbums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return DriftAlbumInfoListTile(album: filteredAlbums[index]); + }), childCount: filteredAlbums.length), ), ); } @@ -331,10 +284,7 @@ class _AlbumSelectionGrid extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionGrid({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -350,11 +300,7 @@ class _AlbumSelectionGrid extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -367,9 +313,7 @@ class _AlbumSelectionGrid extends StatelessWidget { ), itemCount: filteredAlbums.length, itemBuilder: ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); + return DriftAlbumInfoListTile(album: filteredAlbums[index]); }), ), ); @@ -379,9 +323,7 @@ class _AlbumSelectionGrid extends StatelessWidget { class _SelectedAlbumNameChips extends ConsumerWidget { final List selectedBackupAlbums; - const _SelectedAlbumNameChips({ - required this.selectedBackupAlbums, - }); + const _SelectedAlbumNameChips({required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -411,10 +353,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -428,9 +367,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget { class _ExcludedAlbumNameChips extends ConsumerWidget { final List excludedBackupAlbums; - const _ExcludedAlbumNameChips({ - required this.excludedBackupAlbums, - }); + const _ExcludedAlbumNameChips({required this.excludedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -452,18 +389,11 @@ class _ExcludedAlbumNameChips extends ConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -478,10 +408,7 @@ class _SelectAllButton extends ConsumerWidget { final List filteredAlbums; final List selectedBackupAlbums; - const _SelectAllButton({ - required this.filteredAlbums, - required this.selectedBackupAlbums, - }); + const _SelectAllButton({required this.filteredAlbums, required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -505,13 +432,9 @@ class _SelectAllButton extends ConsumerWidget { icon: const Icon(Icons.select_all), label: AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: Text( - "select_all".t(context: context), - ), - ), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text("select_all".t(context: context)), ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), const SizedBox(width: 8.0), @@ -528,9 +451,7 @@ class _SelectAllButton extends ConsumerWidget { : null, icon: const Icon(Icons.deselect), label: Text('deselect_all'.t(context: context)), - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), - ), + style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), ], diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 62d6341fd1..36dbe4e128 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -16,9 +16,7 @@ class DriftUploadDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); return Scaffold( appBar: AppBar( @@ -36,26 +34,18 @@ class DriftUploadDetailPage extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.cloud_off_rounded, - size: 80, - color: context.colorScheme.onSurface.withValues(alpha: 0.3), - ), + Icon(Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3)), const SizedBox(height: 16), Text( "no_uploads_in_progress".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.6), - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.6)), ), ], ), ); } - Widget _buildUploadList( - Map uploadItems, - ) { + Widget _buildUploadList(Map uploadItems) { return ListView.separated( addAutomaticKeepAlives: true, padding: const EdgeInsets.all(16), @@ -68,10 +58,7 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Widget _buildUploadCard( - BuildContext context, - DriftUploadStatus item, - ) { + Widget _buildUploadCard(BuildContext context, DriftUploadStatus item) { final isCompleted = item.progress >= 1.0; final double progressPercentage = (item.progress * 100).clamp(0, 100); @@ -79,19 +66,12 @@ class DriftUploadDetailPage extends ConsumerWidget { elevation: 0, color: item.isFailed != null ? context.colorScheme.errorContainer : context.colorScheme.surfaceContainer, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), child: InkWell( onTap: () => _showFileDetailDialog(context, item), - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( @@ -105,9 +85,7 @@ class DriftUploadDetailPage extends ConsumerWidget { children: [ Text( path.basename(item.filename), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -166,18 +144,11 @@ class DriftUploadDetailPage extends ConsumerWidget { ), ), if (isCompleted) - Icon( - Icons.check_circle_rounded, - size: 28, - color: context.colorScheme.primary, - ) + Icon(Icons.check_circle_rounded, size: 28, color: context.colorScheme.primary) else Text( percentage.toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - fontSize: 10, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold, fontSize: 10), ), ], ), @@ -192,10 +163,7 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Future _showFileDetailDialog( - BuildContext context, - DriftUploadStatus item, - ) async { + Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { showDialog( context: context, builder: (context) => FileDetailDialog(uploadStatus: item), @@ -206,10 +174,7 @@ class DriftUploadDetailPage extends ConsumerWidget { class FileDetailDialog extends ConsumerWidget { final DriftUploadStatus uploadStatus; - const FileDetailDialog({ - super.key, - required this.uploadStatus, - }); + const FileDetailDialog({super.key, required this.uploadStatus}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -217,29 +182,17 @@ class FileDetailDialog extends ConsumerWidget { insetPadding: const EdgeInsets.all(20), backgroundColor: context.colorScheme.surfaceContainerLow, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.2), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), ), title: Row( children: [ - Icon( - Icons.info_outline, - color: context.primaryColor, - size: 24, - ), + Icon(Icons.info_outline, color: context.primaryColor, size: 24), const SizedBox(width: 8), Expanded( child: Text( "details".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], @@ -250,10 +203,7 @@ class FileDetailDialog extends ConsumerWidget { future: _getAssetDetails(ref, uploadStatus.taskId), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const SizedBox( - height: 200, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); } final asset = snapshot.data; @@ -270,18 +220,11 @@ class FileDetailDialog extends ConsumerWidget { width: 128, height: 128, decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: 0.2), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: asset != null - ? Thumbnail( - asset: asset, - size: const Size(512, 512), - fit: BoxFit.cover, - ) + ? Thumbnail(asset: asset, size: const Size(512, 512), fit: BoxFit.cover) : null, ), ), @@ -289,44 +232,14 @@ class FileDetailDialog extends ConsumerWidget { const SizedBox(height: 24), if (asset != null) ...[ _buildInfoSection(context, [ - _buildInfoRow( - context, - "Filename", - path.basename(uploadStatus.filename), - ), - _buildInfoRow( - context, - "Local ID", - asset.id, - ), - _buildInfoRow( - context, - "File Size", - formatHumanReadableBytes(uploadStatus.fileSize, 2), - ), + _buildInfoRow(context, "Filename", path.basename(uploadStatus.filename)), + _buildInfoRow(context, "Local ID", asset.id), + _buildInfoRow(context, "File Size", formatHumanReadableBytes(uploadStatus.fileSize, 2)), if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"), - if (asset.height != null) - _buildInfoRow( - context, - "Height", - "${asset.height}px", - ), - _buildInfoRow( - context, - "Created At", - asset.createdAt.toString(), - ), - _buildInfoRow( - context, - "Updated At", - asset.updatedAt.toString(), - ), - if (asset.checksum != null) - _buildInfoRow( - context, - "Checksum", - asset.checksum!, - ), + if (asset.height != null) _buildInfoRow(context, "Height", "${asset.height}px"), + _buildInfoRow(context, "Created At", asset.createdAt.toString()), + _buildInfoRow(context, "Updated At", asset.updatedAt.toString()), + if (asset.checksum != null) _buildInfoRow(context, "Checksum", asset.checksum!), ]), ], ], @@ -340,39 +253,23 @@ class FileDetailDialog extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(), child: Text( "close".t(), - style: TextStyle( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], ); } - Widget _buildInfoSection( - BuildContext context, - List children, - ) { + Widget _buildInfoSection(BuildContext context, List children) { return Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, - borderRadius: const BorderRadius.all( - Radius.circular(12), - ), - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...children, - ], + borderRadius: const BorderRadius.all(Radius.circular(12)), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [...children]), ); } @@ -405,10 +302,7 @@ class FileDetailDialog extends ConsumerWidget { ); } - Future _getAssetDetails( - WidgetRef ref, - String localAssetId, - ) async { + Future _getAssetDetails(WidgetRef ref, String localAssetId) async { try { final repository = ref.read(localAssetRepository); return await repository.getById(localAssetId); diff --git a/mobile/lib/pages/backup/failed_backup_status.page.dart b/mobile/lib/pages/backup/failed_backup_status.page.dart index 8d0faf2d22..b533895cd7 100644 --- a/mobile/lib/pages/backup/failed_backup_status.page.dart +++ b/mobile/lib/pages/backup/failed_backup_status.page.dart @@ -25,9 +25,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: ListView.builder( @@ -37,19 +35,13 @@ class FailedBackupStatusPage extends HookConsumerWidget { var errorAsset = errorBackupList.elementAt(index); return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15), // if you need this ), - side: BorderSide( - color: Colors.black12, - width: 1, - ), + side: BorderSide(color: Colors.black12, width: 1), ), elevation: 0, child: Row( @@ -57,12 +49,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 100, - minHeight: 100, - maxWidth: 100, - maxHeight: 150, - ), + constraints: const BoxConstraints(minWidth: 100, minHeight: 100, maxWidth: 100, maxHeight: 150), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(15), @@ -71,11 +58,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { clipBehavior: Clip.hardEdge, child: Image( fit: BoxFit.cover, - image: ImmichLocalThumbnailProvider( - asset: errorAsset.asset, - height: 512, - width: 512, - ), + image: ImmichLocalThumbnailProvider(asset: errorAsset.asset, height: 512, width: 512), ), ), ), @@ -91,20 +74,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { children: [ Text( DateFormat.yMMMMd().format( - DateTime.parse( - errorAsset.fileCreatedAt.toString(), - ).toLocal(), + DateTime.parse(errorAsset.fileCreatedAt.toString()).toLocal(), ), style: TextStyle( fontWeight: FontWeight.w600, color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), - Icon( - Icons.error, - color: Colors.red.withAlpha(200), - size: 18, - ), + Icon(Icons.error, color: Colors.red.withAlpha(200), size: 18), ], ), Padding( @@ -113,10 +90,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { errorAsset.fileName, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), Text( diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 203df2d503..1a1955af40 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -16,9 +16,7 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; @RoutePage() class ActivitiesPage extends HookConsumerWidget { - const ActivitiesPage({ - super.key, - }); + const ActivitiesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -60,9 +58,7 @@ class ActivitiesPage extends HookConsumerWidget { itemBuilder: (context, index) { // Additional vertical gap after the last element if (index == data.length) { - return const SizedBox( - height: 80, - ); + return const SizedBox(height: 80); } final activity = data[index]; @@ -73,8 +69,9 @@ class ActivitiesPage extends HookConsumerWidget { child: DismissibleActivity( activity.id, ActivityTile(activity), - onDismiss: - canDelete ? (activityId) async => await activityNotifier.removeActivity(activity.id) : null, + onDismiss: canDelete + ? (activityId) async => await activityNotifier.removeActivity(activity.id) + : null, ), ); }, diff --git a/mobile/lib/pages/common/app_log.page.dart b/mobile/lib/pages/common/app_log.page.dart index 359a541de0..fe0c0ea442 100644 --- a/mobile/lib/pages/common/app_log.page.dart +++ b/mobile/lib/pages/common/app_log.page.dart @@ -12,17 +12,13 @@ import 'package:intl/intl.dart'; @RoutePage() class AppLogPage extends HookConsumerWidget { - const AppLogPage({ - super.key, - }); + const AppLogPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final immichLogger = LogService.I; final shouldReload = useState(false); - final logMessages = useFuture( - useMemoized(() => immichLogger.getMessages(), [shouldReload.value]), - ); + final logMessages = useFuture(useMemoized(() => immichLogger.getMessages(), [shouldReload.value])); Widget colorStatusIndicator(Color color) { return Column( @@ -31,38 +27,29 @@ class AppLogPage extends HookConsumerWidget { Container( width: 10, height: 10, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), ], ); } Widget buildLeadingIcon(LogLevel level) => switch (level) { - LogLevel.info => colorStatusIndicator(context.primaryColor), - LogLevel.severe => colorStatusIndicator(Colors.redAccent), - LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), - _ => colorStatusIndicator(Colors.grey), - }; + LogLevel.info => colorStatusIndicator(context.primaryColor), + LogLevel.severe => colorStatusIndicator(Colors.redAccent), + LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), + _ => colorStatusIndicator(Colors.grey), + }; Color getTileColor(LogLevel level) => switch (level) { - LogLevel.info => Colors.transparent, - LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), - LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), - _ => context.primaryColor.withValues(alpha: 0.1), - }; + LogLevel.info => Colors.transparent, + LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), + LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), + _ => context.primaryColor.withValues(alpha: 0.1), + }; return Scaffold( appBar: AppBar( - title: const Text( - "Logs", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16.0, - ), - ), + title: const Text("Logs", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), scrolledUnderElevation: 1, elevation: 2, actions: [ @@ -81,12 +68,7 @@ class AppLogPage extends HookConsumerWidget { Builder( builder: (BuildContext iconContext) { return IconButton( - icon: Icon( - Icons.share_rounded, - color: context.primaryColor, - semanticLabel: "Share logs", - size: 20.0, - ), + icon: Icon(Icons.share_rounded, color: context.primaryColor, semanticLabel: "Share logs", size: 20.0), onPressed: () { ImmichLogger.shareLogs(iconContext); }, @@ -98,10 +80,7 @@ class AppLogPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: const Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - ), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20.0), ), centerTitle: true, ), @@ -113,11 +92,7 @@ class AppLogPage extends HookConsumerWidget { itemBuilder: (context, index) { var logMessage = logMessages.data![index]; return ListTile( - onTap: () => context.pushRoute( - AppLogDetailRoute( - logMessage: logMessage, - ), - ), + onTap: () => context.pushRoute(AppLogDetailRoute(logMessage: logMessage)), trailing: const Icon(Icons.arrow_forward_ios_rounded), visualDensity: VisualDensity.compact, dense: true, @@ -125,18 +100,11 @@ class AppLogPage extends HookConsumerWidget { minLeadingWidth: 10, title: Text( truncateLogMessage(logMessage.message, 4), - style: TextStyle( - fontSize: 14.0, - color: context.colorScheme.onSurface, - fontFamily: "Inconsolata", - ), + style: TextStyle(fontSize: 14.0, color: context.colorScheme.onSurface, fontFamily: "Inconsolata"), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.logger}", - style: TextStyle( - fontSize: 12.0, - color: context.colorScheme.onSurfaceSecondary, - ), + style: TextStyle(fontSize: 12.0, color: context.colorScheme.onSurfaceSecondary), ), leading: buildLeadingIcon(logMessage.level), ); diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index d8647ca8e2..a9cf634faf 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -27,11 +27,7 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( header, - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), IconButton( @@ -41,38 +37,26 @@ class AppLogDetailPage extends HookConsumerWidget { SnackBar( content: Text( "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ], ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( text, - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -91,29 +75,19 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "FROM", - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( context1.toString(), - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -123,20 +97,14 @@ class AppLogDetailPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("Log Detail"), - ), + appBar: AppBar(title: const Text("Log Detail")), body: SafeArea( child: ListView( children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()), - if (logMessage.stack != null) - buildTextWithCopyButton( - "STACK TRACE", - logMessage.stack.toString(), - ), + if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), ), diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 21464e39fa..45392a38f6 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -49,11 +49,9 @@ class _ChangeExperiencePageState extends ConsumerState { // Cancel uploads await Store.put(StoreKey.backgroundBackup, false); - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onBatteryInfo: () {}, - onError: (_) {}, - ); + ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onBatteryInfo: () {}, onError: (_) {}); ref.read(backupProvider.notifier).setAutoBackup(false); ref.read(backupProvider.notifier).cancelBackup(); ref.read(manualUploadProvider.notifier).cancelBackup(); @@ -65,14 +63,8 @@ class _ChangeExperiencePageState extends ConsumerState { if (permission.isGranted) { await ref.read(backgroundSyncProvider).syncLocal(full: true); - await migrateDeviceAssetToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); - await migrateBackupAlbumsToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); + await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider)); } } else { await ref.read(backgroundSyncProvider).cancel(); @@ -98,16 +90,8 @@ class _ChangeExperiencePageState extends ConsumerState { AnimatedSwitcher( duration: Durations.long4, child: hasMigrated - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - size: 48.0, - ) - : const SizedBox( - width: 50.0, - height: 50.0, - child: CircularProgressIndicator(), - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green, size: 48.0) + : const SizedBox(width: 50.0, height: 50.0, child: CircularProgressIndicator()), ), const SizedBox(height: 16.0), Center( diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index 02e1ea18d4..5a0d4154f8 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -20,10 +20,7 @@ import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart'; class CreateAlbumPage extends HookConsumerWidget { final List? assets; - const CreateAlbumPage({ - super.key, - this.assets, - }); + const CreateAlbumPage({super.key, this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -32,9 +29,7 @@ class CreateAlbumPage extends HookConsumerWidget { final albumDescriptionTextFieldFocusNode = useFocusNode(); final isAlbumTitleTextFieldFocus = useState(false); final isAlbumTitleEmpty = useState(true); - final selectedAssets = useState>( - assets != null ? Set.from(assets!) : const {}, - ); + final selectedAssets = useState>(assets != null ? Set.from(assets!) : const {}); void onBackgroundTapped() { albumTitleTextFieldFocusNode.unfocus(); @@ -50,10 +45,7 @@ class CreateAlbumPage extends HookConsumerWidget { onSelectPhotosButtonPressed() async { AssetSelectionPageResult? selectedAsset = await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: selectedAssets.value, - canDeselect: true, - ), + AlbumAssetSelectionRoute(existingAssets: selectedAssets.value, canDeselect: true), ); if (selectedAsset == null) { selectedAssets.value = const {}; @@ -64,10 +56,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumTitleTextField( isAlbumTitleEmpty: isAlbumTitleEmpty, albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, @@ -79,10 +68,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumViewerEditableDescription( albumDescription: '', descriptionFocusNode: albumDescriptionTextFieldFocusNode, @@ -95,10 +81,7 @@ class CreateAlbumPage extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 200, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).tr(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).tr(), ), ); } @@ -115,18 +98,11 @@ class CreateAlbumPage extends HookConsumerWidget { style: FilledButton.styleFrom( alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotosButtonPressed, - icon: Icon( - Icons.add_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( @@ -174,17 +150,12 @@ class CreateAlbumPage extends HookConsumerWidget { crossAxisSpacing: 5.0, mainAxisSpacing: 5, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return GestureDetector( - onTap: onBackgroundTapped, - child: SharedAlbumThumbnailImage( - asset: selectedAssets.value.elementAt(index), - ), - ); - }, - childCount: selectedAssets.value.length, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + return GestureDetector( + onTap: onBackgroundTapped, + child: SharedAlbumThumbnailImage(asset: selectedAssets.value.elementAt(index)), + ); + }, childCount: selectedAssets.value.length), ), ); } @@ -194,10 +165,9 @@ class CreateAlbumPage extends HookConsumerWidget { Future createAlbum() async { onBackgroundTapped(); - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.read(albumTitleProvider), - selectedAssets.value, - ); + var newAlbum = await ref + .watch(albumProvider.notifier) + .createAlbum(ref.read(albumTitleProvider), selectedAssets.value); if (newAlbum != null) { ref.read(albumProvider.notifier).refreshRemoteAlbums(); @@ -220,9 +190,7 @@ class CreateAlbumPage extends HookConsumerWidget { }, icon: const Icon(Icons.close_rounded), ), - title: const Text( - 'create_album', - ).tr(), + title: const Text('create_album').tr(), actions: [ TextButton( onPressed: albumTitleController.text.isNotEmpty ? createAlbum : null, diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_panel.dart index 38212d5486..0775f5b4e4 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_panel.dart @@ -6,22 +6,13 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; class DownloadPanel extends ConsumerWidget { - const DownloadPanel({ - super.key, - }); + const DownloadPanel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showProgress = ref.watch( - downloadStateProvider.select((state) => state.showProgress), - ); + final showProgress = ref.watch(downloadStateProvider.select((state) => state.showProgress)); - final tasks = ref - .watch( - downloadStateProvider.select((state) => state.taskProgress), - ) - .entries - .toList(); + final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); onCancelDownload(String id) { ref.watch(downloadStateProvider.notifier).cancelDownload(id); @@ -74,47 +65,35 @@ class DownloadTaskTile extends StatelessWidget { final progressPercent = (progress * 100).round(); String getStatusText() => switch (status) { - TaskStatus.running => 'downloading'.tr(), - TaskStatus.complete => 'download_complete'.tr(), - TaskStatus.failed => 'download_failed'.tr(), - TaskStatus.canceled => 'download_canceled'.tr(), - TaskStatus.paused => 'download_paused'.tr(), - TaskStatus.enqueued => 'download_enqueue'.tr(), - TaskStatus.notFound => 'download_notfound'.tr(), - TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), - }; + TaskStatus.running => 'downloading'.tr(), + TaskStatus.complete => 'download_complete'.tr(), + TaskStatus.failed => 'download_failed'.tr(), + TaskStatus.canceled => 'download_canceled'.tr(), + TaskStatus.paused => 'download_paused'.tr(), + TaskStatus.enqueued => 'download_enqueue'.tr(), + TaskStatus.notFound => 'download_notfound'.tr(), + TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), + }; return SizedBox( key: const ValueKey('download_progress'), width: context.width - 32, child: Card( clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: ListTile( minVerticalPadding: 18, leading: const Icon(Icons.video_file_outlined), - title: Text( - getStatusText(), - style: context.textTheme.labelLarge, - ), + title: Text(getStatusText(), style: context.textTheme.labelLarge), trailing: IconButton( icon: Icon(Icons.close, color: context.colorScheme.onError), onPressed: onCancelDownload, - style: ElevatedButton.styleFrom( - backgroundColor: context.colorScheme.error.withAlpha(200), - ), + style: ElevatedButton.styleFrom(backgroundColor: context.colorScheme.error.withAlpha(200)), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - fileName, - style: context.textTheme.labelMedium, - ), + Text(fileName, style: context.textTheme.labelMedium), Row( children: [ Expanded( @@ -125,10 +104,7 @@ class DownloadTaskTile extends StatelessWidget { ), ), const SizedBox(width: 8), - Text( - '$progressPercent%', - style: context.textTheme.labelSmall, - ), + Text('$progressPercent%', style: context.textTheme.labelSmall), ], ), ], diff --git a/mobile/lib/pages/common/gallery_stacked_children.dart b/mobile/lib/pages/common/gallery_stacked_children.dart index eafc325049..7145bc2553 100644 --- a/mobile/lib/pages/common/gallery_stacked_children.dart +++ b/mobile/lib/pages/common/gallery_stacked_children.dart @@ -36,11 +36,7 @@ class GalleryStackedChildren extends HookConsumerWidget { shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: stackElements.length, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemBuilder: (context, index) { final currentAsset = stackElements.elementAt(index); final assetId = currentAsset.remoteId; @@ -63,9 +59,7 @@ class GalleryStackedChildren extends HookConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 05389018da..3c279dfcd2 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -87,11 +87,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (index < totalAssets.value && index >= 0) { final asset = loadAsset(index); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), context, onError: onError, ); @@ -103,23 +99,20 @@ class GalleryViewerPage extends HookConsumerWidget { } } - useEffect( - () { - if (ref.read(showControlsProvider)) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); - } + useEffect(() { + if (ref.read(showControlsProvider)) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + } - // Delay this a bit so we can finish loading the page - Timer(const Duration(milliseconds: 400), () { - precacheNextImage(currentIndex.value + 1); - }); + // Delay this a bit so we can finish loading the page + Timer(const Duration(milliseconds: 400), () { + precacheNextImage(currentIndex.value + 1); + }); - return null; - }, - const [], - ); + return null; + }, const []); useEffect(() { final asset = loadAsset(currentIndex.value); @@ -136,9 +129,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 1), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -147,9 +138,7 @@ class GalleryViewerPage extends HookConsumerWidget { } } return null; - }, [ - ref.watch(castProvider).isCasting, - ]); + }, [ref.watch(castProvider).isCasting]); void showInfo() { final asset = ref.read(currentAssetProvider); @@ -157,9 +146,7 @@ class GalleryViewerPage extends HookConsumerWidget { return; } showModalBottomSheet( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), barrierColor: Colors.transparent, isScrollControlled: true, showDragHandle: true, @@ -174,20 +161,10 @@ class GalleryViewerPage extends HookConsumerWidget { expand: false, builder: (context, scrollController) { return Padding( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), - child: ref.watch(appSettingsServiceProvider).getSetting( - AppSettingsEnum.advancedTroubleshooting, - ) - ? AdvancedBottomSheet( - assetDetail: asset, - scrollController: scrollController, - ) - : DetailPanel( - asset: asset, - scrollController: scrollController, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), + child: ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.advancedTroubleshooting) + ? AdvancedBottomSheet(assetDetail: asset, scrollController: scrollController) + : DetailPanel(asset: asset, scrollController: scrollController), ); }, ); @@ -258,10 +235,7 @@ class GalleryViewerPage extends HookConsumerWidget { tightMode: true, initialScale: PhotoViewComputedScale.contained * 0.99, minScale: PhotoViewComputedScale.contained * 0.99, - errorBuilder: (context, error, stackTrace) => ImmichImage( - asset, - fit: BoxFit.contain, - ), + errorBuilder: (context, error, stackTrace) => ImmichImage(asset, fit: BoxFit.contain), ); } @@ -283,11 +257,7 @@ class GalleryViewerPage extends HookConsumerWidget { asset: asset, image: Image( key: ValueKey(asset), - image: ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + image: ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), fit: BoxFit.contain, height: context.height, width: context.width, @@ -342,17 +312,8 @@ class GalleryViewerPage extends HookConsumerWidget { child: Stack( fit: StackFit.expand, children: [ - BackdropFilter( - filter: ui.ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - ), - ImmichThumbnail( - key: ValueKey(asset), - asset: asset, - fit: BoxFit.contain, - ), + BackdropFilter(filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)), + ImmichThumbnail(key: ValueKey(asset), asset: asset, fit: BoxFit.contain), ], ), ); @@ -361,9 +322,9 @@ class GalleryViewerPage extends HookConsumerWidget { scrollPhysics: isZoomed.value ? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in : (Platform.isIOS - ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - ), + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics() // Use heavy physics for Android + ), itemCount: totalAssets.value, scrollDirection: Axis.horizontal, onPageChanged: (value, _) { @@ -401,9 +362,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -416,10 +375,7 @@ class GalleryViewerPage extends HookConsumerWidget { top: 0, left: 0, right: 0, - child: GalleryAppBar( - key: const ValueKey('app-bar'), - showInfo: showInfo, - ), + child: GalleryAppBar(key: const ValueKey('app-bar'), showInfo: showInfo), ), Positioned( bottom: 0, diff --git a/mobile/lib/pages/common/headers_settings.page.dart b/mobile/lib/pages/common/headers_settings.page.dart index 0f4ab882c8..4cf683b4d9 100644 --- a/mobile/lib/pages/common/headers_settings.page.dart +++ b/mobile/lib/pages/common/headers_settings.page.dart @@ -79,10 +79,8 @@ class HeaderSettingsPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), itemCount: list.length, itemBuilder: (ctx, index) => list[index], - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), - child: Divider(), - ), + separatorBuilder: (context, index) => + const Padding(padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), child: Divider()), ), ), ); @@ -109,12 +107,9 @@ class HeaderKeyValueSettings extends StatelessWidget { final SettingsHeader header; final Function() onRemove; - HeaderKeyValueSettings({ - super.key, - required this.header, - required this.onRemove, - }) : keyController = TextEditingController(text: header.key), - valueController = TextEditingController(text: header.value); + HeaderKeyValueSettings({super.key, required this.header, required this.onRemove}) + : keyController = TextEditingController(text: header.key), + valueController = TextEditingController(text: header.value); String? emptyFieldValidator(String? value) { if (value == null || value.isEmpty) { @@ -150,9 +145,7 @@ class HeaderKeyValueSettings extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 8), child: IconButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), color: Colors.red[400], onPressed: onRemove, icon: const Icon(Icons.delete_outline), diff --git a/mobile/lib/pages/common/large_leading_tile.dart b/mobile/lib/pages/common/large_leading_tile.dart index d36e296429..4563834473 100644 --- a/mobile/lib/pages/common/large_leading_tile.dart +++ b/mobile/lib/pages/common/large_leading_tile.dart @@ -8,10 +8,7 @@ class LargeLeadingTile extends StatelessWidget { required this.onTap, required this.title, this.subtitle, - this.leadingPadding = const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16.0, - ), + this.leadingPadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16.0), this.borderRadius = 20.0, this.trailing, this.selected = false, @@ -47,18 +44,12 @@ class LargeLeadingTile extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: leadingPadding, - child: leading, - ), + Padding(padding: leadingPadding, child: leading), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - width: context.width * 0.6, - child: title, - ), + SizedBox(width: context.width * 0.6, child: title), subtitle ?? const SizedBox.shrink(), ], ), diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index 0dbaf6125c..d8b6db2276 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -78,17 +78,15 @@ class NativeVideoViewerPage extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } // Use a network URL for the video player controller final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final isOriginalVideo = - ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loadOriginalVideo); + final isOriginalVideo = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.loadOriginalVideo); final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = asset.livePhotoVideoId != null ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' @@ -101,30 +99,24 @@ class NativeVideoViewerPage extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.fileName}: $error', - ); + log.severe('Error creating video source for asset ${asset.fileName}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(asset.aspectRatio); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.fileName}: $error', - ); - } - }, - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.fileName}: $error'); + } + }); void checkIfBuffering() { if (!context.mounted) { @@ -134,8 +126,9 @@ class NativeVideoViewerPage extends HookConsumerWidget { final videoPlayback = ref.read(videoPlaybackValueProvider); if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -322,48 +315,42 @@ class NativeVideoViewerPage extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -393,12 +380,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index d18a7a1133..d7ecb7e582 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -18,67 +18,31 @@ import 'package:immich_mobile/widgets/settings/preference_settings/preference_se import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - beta( - 'beta_sync', - Icons.sync_outlined, - "beta_sync_subtitle", - ), - advanced( - 'advanced', - Icons.build_outlined, - "advanced_settings_tile_subtitle", - ), - assetViewer( - 'asset_viewer_settings_title', - Icons.image_outlined, - "asset_viewer_settings_subtitle", - ), - backup( - 'backup', - Icons.cloud_upload_outlined, - "backup_setting_subtitle", - ), - languages( - 'language', - Icons.language, - "setting_languages_subtitle", - ), - networking( - 'networking_settings', - Icons.wifi, - "networking_subtitle", - ), - notifications( - 'notifications', - Icons.notifications_none_rounded, - "setting_notifications_subtitle", - ), - preferences( - 'preferences_settings_title', - Icons.interests_outlined, - "preferences_settings_subtitle", - ), - timeline( - 'asset_list_settings_title', - Icons.auto_awesome_mosaic_outlined, - "asset_list_settings_subtitle", - ); + beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), + advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), + assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_setting_subtitle"), + languages('language', Icons.language, "setting_languages_subtitle"), + networking('networking_settings', Icons.wifi, "networking_subtitle"), + notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), + preferences('preferences_settings_title', Icons.interests_outlined, "preferences_settings_subtitle"), + timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"); final String title; final String subtitle; final IconData icon; Widget get widget => switch (this) { - SettingSection.beta => const _BetaLandscapeToggle(), - SettingSection.advanced => const AdvancedSettings(), - SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), - SettingSection.languages => const LanguageSettings(), - SettingSection.networking => const NetworkingSettings(), - SettingSection.notifications => const NotificationSetting(), - SettingSection.preferences => const PreferenceSetting(), - SettingSection.timeline => const AssetListSettings(), - }; + SettingSection.beta => const _BetaLandscapeToggle(), + SettingSection.advanced => const AdvancedSettings(), + SettingSection.assetViewer => const AssetViewerSettings(), + SettingSection.backup => const BackupSettings(), + SettingSection.languages => const LanguageSettings(), + SettingSection.networking => const NetworkingSettings(), + SettingSection.notifications => const NotificationSetting(), + SettingSection.preferences => const PreferenceSetting(), + SettingSection.timeline => const AssetListSettings(), + }; const SettingSection(this.title, this.icon, this.subtitle); } @@ -91,10 +55,7 @@ class SettingsPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: const Text('settings').tr(), - ), + appBar: AppBar(centerTitle: false, title: const Text('settings').tr()), body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), ); } @@ -164,10 +125,7 @@ class _TabletLayout extends HookWidget { ), ), const VerticalDivider(width: 1), - Expanded( - flex: 4, - child: selectedSection.value.widget, - ), + Expanded(flex: 4, child: selectedSection.value.widget), ], ); } @@ -198,10 +156,7 @@ class SettingsSubPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: Text(section.title).tr(), - ), + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), body: section.widget, ); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 47cd64f7f9..2bda4f90f9 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -43,31 +43,26 @@ class SplashScreenPageState extends ConsumerState { final accessToken = Store.tryGet(StoreKey.accessToken); if (accessToken != null && serverUrl != null && endpoint != null) { - ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( - (a) => { - log.info('Successfully updated auth info with access token: $accessToken'), - }, + ref + .read(authProvider.notifier) + .saveAuthInfo(accessToken: accessToken) + .then( + (a) => {log.info('Successfully updated auth info with access token: $accessToken')}, onError: (exception) => { - log.severe( - 'Failed to update auth info with access token: $accessToken', - ), + log.severe('Failed to update auth info with access token: $accessToken'), ref.read(authProvider.notifier).logout(), context.replaceRoute(const LoginRoute()), }, ); } else { - log.severe( - 'Missing crucial offline login info - Logging out completely', - ); + log.severe('Missing crucial offline login info - Logging out completely'); ref.read(authProvider.notifier).logout(); context.replaceRoute(const LoginRoute()); return; } if (context.router.current.name == SplashScreenRoute.name) { - context.replaceRoute( - Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(), - ); + context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); } if (Store.isBetaTimelineEnabled) { @@ -85,11 +80,7 @@ class SplashScreenPageState extends ConsumerState { Widget build(BuildContext context) { return const Scaffold( body: Center( - child: Image( - image: AssetImage('assets/immich-logo.png'), - width: 80, - filterQuality: FilterQuality.high, - ), + child: Image(image: AssetImage('assets/immich-logo.png'), width: 80, filterQuality: FilterQuality.high), ), ); } diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index 676b1db11b..ef637ba1c8 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -36,9 +36,7 @@ class TabControllerPage extends HookConsumerWidget { width: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), + valueColor: AlwaysStoppedAnimation(context.primaryColor), ), ), ), @@ -65,51 +63,31 @@ class TabControllerPage extends HookConsumerWidget { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), + icon: const Icon(Icons.photo_library_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_library, color: context.primaryColor), ), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), + icon: const Icon(Icons.photo_album_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingRemoteAlbums, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), + icon: const Icon(Icons.space_dashboard_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ), ]; @@ -125,13 +103,7 @@ class TabControllerPage extends HookConsumerWidget { Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), selectedIndex: tabsRouter.activeIndex, @@ -142,17 +114,9 @@ class TabControllerPage extends HookConsumerWidget { final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( - routes: [ - const PhotosRoute(), - SearchRoute(), - const AlbumsRoute(), - const LibraryRoute(), - ], + routes: [const PhotosRoute(), SearchRoute(), const AlbumsRoute(), const LibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 3961a8b14b..e06f7ca441 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -56,56 +56,30 @@ class _TabShellPageState extends ConsumerState { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), - selectedIcon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_library_outlined), + selectedIcon: Icon(Icons.photo_library, color: context.primaryColor), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), - selectedIcon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_album_outlined), + selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), - selectedIcon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.space_dashboard_outlined), + selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ]; Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), selectedIndex: tabsRouter.activeIndex, @@ -115,17 +89,9 @@ class _TabShellPageState extends ConsumerState { } return AutoTabsRouter( - routes: [ - const MainTimelineRoute(), - DriftSearchRoute(), - const DriftAlbumsRoute(), - const DriftLibraryRoute(), - ], + routes: [const MainTimelineRoute(), DriftSearchRoute(), const DriftAlbumsRoute(), const DriftLibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( @@ -142,10 +108,7 @@ class _TabShellPageState extends ConsumerState { ], ) : child, - bottomNavigationBar: _BottomNavigationBar( - tabsRouter: tabsRouter, - destinations: navigationDestinations, - ), + bottomNavigationBar: _BottomNavigationBar(tabsRouter: tabsRouter, destinations: navigationDestinations), ), ); }, @@ -175,10 +138,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { } class _BottomNavigationBar extends ConsumerWidget { - const _BottomNavigationBar({ - required this.tabsRouter, - required this.destinations, - }); + const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index 97ef069e7b..35fd615800 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -32,20 +32,10 @@ class CropImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute( - EditImageRoute( - asset: asset, - image: croppedImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); }, ), ], @@ -60,11 +50,7 @@ class CropImagePage extends HookWidget { padding: const EdgeInsets.only(top: 20), width: constraints.maxWidth * 0.9, height: constraints.maxHeight * 0.6, - child: CropImage( - controller: cropController, - image: image, - gridColor: Colors.white, - ), + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), ), Expanded( child: Container( @@ -81,28 +67,18 @@ class CropImagePage extends HookWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - bottom: 10, - ), + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( - icon: Icon( - Icons.rotate_left, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateLeft(); }, ), IconButton( - icon: Icon( - Icons.rotate_right, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateRight(); }, @@ -178,17 +154,14 @@ class _AspectRatioButton extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: Icon( - switch (label) { - 'Free' => Icons.crop_free_rounded, - '1:1' => Icons.crop_square_rounded, - '16:9' => Icons.crop_16_9_rounded, - '3:2' => Icons.crop_3_2_rounded, - '7:5' => Icons.crop_7_5_rounded, - _ => Icons.crop_free_rounded, - }, - color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color, - ), + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), onPressed: () { cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); aspectRatio.value = ratio; diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index 70940e9552..c9ab014456 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -29,51 +29,34 @@ class EditImagePage extends ConsumerWidget { final Image image; final bool isEdited; - const EditImagePage({ - super.key, - required this.asset, - required this.image, - required this.isEdited, - }); + const EditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); Future _imageToUint8List(Image image) async { final Completer completer = Completer(); - image.image.resolve(const ImageConfiguration()).addListener( - ImageStreamListener( - (ImageInfo info, bool _) { - info.image.toByteData(format: ImageByteFormat.png).then((byteData) { - if (byteData != null) { - completer.complete(byteData.buffer.asUint8List()); - } else { - completer.completeError('Failed to convert image to bytes'); - } - }); - }, - onError: (exception, stackTrace) => completer.completeError(exception), - ), + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), ); return completer.future; } - Future _saveEditedImage( - BuildContext context, - Asset asset, - Image image, - WidgetRef ref, - ) async { + Future _saveEditedImage(BuildContext context, Asset asset, Image image, WidgetRef ref) async { try { final Uint8List imageData = await _imageToUint8List(image); - await ref.read(fileMediaRepositoryProvider).saveImage( - imageData, - title: "${p.withoutExtension(asset.fileName)}_edited.jpg", - ); + await ref + .read(fileMediaRepositoryProvider) + .saveImage(imageData, title: "${p.withoutExtension(asset.fileName)}_edited.jpg"); await ref.read(albumProvider.notifier).refreshDeviceAlbums(); context.navigator.popUntil((route) => route.isFirst); - ImmichToast.show( - durationInSecond: 3, - context: context, - msg: 'Image Saved!', - gravity: ToastGravity.CENTER, - ); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!', gravity: ToastGravity.CENTER); } catch (e) { ImmichToast.show( durationInSecond: 6, @@ -91,37 +74,23 @@ class EditImagePage extends ConsumerWidget { title: Text("edit".tr()), backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( - icon: Icon( - Icons.close_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), onPressed: () => context.navigator.popUntil((route) => route.isFirst), ), actions: [ TextButton( onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, - child: Text( - "save_to_gallery".tr(), - style: TextStyle( - color: isEdited ? context.primaryColor : Colors.grey, - ), - ), + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), ), ], ), backgroundColor: context.scaffoldBackgroundColor, body: Center( child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: context.height * 0.7, - maxWidth: context.width * 0.9, - ), + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), child: Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.2), @@ -132,13 +101,8 @@ class EditImagePage extends ConsumerWidget { ], ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), - child: Image( - image: image.image, - fit: BoxFit.contain, - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), ), ), ), @@ -148,9 +112,7 @@ class EditImagePage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( color: context.scaffoldBackgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(30), - ), + borderRadius: const BorderRadius.all(Radius.circular(30)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -159,15 +121,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.crop_rotate_rounded, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - CropImageRoute(asset: asset, image: image), - ); + context.pushRoute(CropImageRoute(asset: asset, image: image)); }, ), Text("crop".tr(), style: context.textTheme.displayMedium), @@ -177,18 +133,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.filter, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - FilterImageRoute( - asset: asset, - image: image, - ), - ); + context.pushRoute(FilterImageRoute(asset: asset, image: image)); }, ), Text("filter".tr(), style: context.textTheme.displayMedium), diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index 3dc8d28a94..6d41b4c5b8 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -18,21 +18,14 @@ class FilterImagePage extends HookWidget { final Image image; final Asset asset; - const FilterImagePage({ - super.key, - required this.image, - required this.asset, - }); + const FilterImagePage({super.key, required this.image, required this.asset}); @override Widget build(BuildContext context) { final colorFilter = useState(filters[0]); final selectedFilterIndex = useState(0); - Future createFilteredImage( - ui.Image inputImage, - ColorFilter filter, - ) { + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { final completer = Completer(); final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); final recorder = ui.PictureRecorder(); @@ -55,11 +48,13 @@ class FilterImagePage extends HookWidget { Future applyFilterAndConvert(ColorFilter filter) async { final completer = Completer(); - image.image.resolve(ImageConfiguration.empty).addListener( - ImageStreamListener((ImageInfo info, bool _) { - completer.complete(info.image); - }), - ); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); final uiImage = await completer.future; final filteredUiImage = await createFilteredImage(uiImage, filter); @@ -76,20 +71,10 @@ class FilterImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final filteredImage = await applyFilterAndConvert(colorFilter.value); - context.pushRoute( - EditImageRoute( - asset: asset, - image: filteredImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true)); }, ), ], @@ -100,10 +85,7 @@ class FilterImagePage extends HookWidget { SizedBox( height: context.height * 0.7, child: Center( - child: ColorFiltered( - colorFilter: colorFilter.value, - child: image, - ), + child: ColorFiltered(colorFilter: colorFilter.value, child: image), ), ), SizedBox( @@ -156,21 +138,14 @@ class _FilterButton extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), child: ColorFiltered( colorFilter: filter, - child: FittedBox( - fit: BoxFit.cover, - child: image, - ), + child: FittedBox(fit: BoxFit.cover, child: image), ), ), ), diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index 2b4aa64f3b..8ca1bb9752 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -16,15 +16,10 @@ class ArchivePage extends HookConsumerWidget { final archiveRenderList = ref.watch(archiveTimelineProvider); final count = archiveRenderList.value?.totalAssets.toString() ?? "?"; return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'archive_page_title', - ).tr(namedArgs: {'count': count}), + title: const Text('archive_page_title').tr(namedArgs: {'count': count}), ); } diff --git a/mobile/lib/pages/library/favorite.page.dart b/mobile/lib/pages/library/favorite.page.dart index 070693fe4a..649d7727d5 100644 --- a/mobile/lib/pages/library/favorite.page.dart +++ b/mobile/lib/pages/library/favorite.page.dart @@ -14,15 +14,10 @@ class FavoritesPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { AppBar buildAppBar() { return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'favorites', - ).tr(), + title: const Text('favorites').tr(), ); } diff --git a/mobile/lib/pages/library/folder/folder.page.dart b/mobile/lib/pages/library/folder/folder.page.dart index d089aace6e..2968bca18e 100644 --- a/mobile/lib/pages/library/folder/folder.page.dart +++ b/mobile/lib/pages/library/folder/folder.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -RecursiveFolder? _findFolderInStructure( - RootFolder rootFolder, - RecursiveFolder targetFolder, -) { +RecursiveFolder? _findFolderInStructure(RootFolder rootFolder, RecursiveFolder targetFolder) { for (final folder in rootFolder.subfolders) { if (targetFolder.path == '/' && folder.path.isEmpty && folder.name == targetFolder.name) { return folder; @@ -49,29 +46,23 @@ class FolderPage extends HookConsumerWidget { final currentFolder = useState(folder); final sortOrder = useState(SortOrder.asc); - useEffect( - () { - if (folder == null) { - ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); - } - return null; - }, - [], - ); + useEffect(() { + if (folder == null) { + ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); + } + return null; + }, []); // Update current folder when root structure changes - useEffect( - () { - if (folder != null && folderState.hasValue) { - final updatedFolder = _findFolderInStructure(folderState.value!, folder!); - if (updatedFolder != null) { - currentFolder.value = updatedFolder; - } + useEffect(() { + if (folder != null && folderState.hasValue) { + final updatedFolder = _findFolderInStructure(folderState.value!, folder!); + if (updatedFolder != null) { + currentFolder.value = updatedFolder; } - return null; - }, - [folderState], - ); + } + return null; + }, [folderState]); void onToggleSortOrder() { final newOrder = sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; @@ -86,38 +77,19 @@ class FolderPage extends HookConsumerWidget { title: Text(currentFolder.value?.name ?? tr("folders")), elevation: 0, centerTitle: false, - actions: [ - IconButton( - icon: const Icon(Icons.swap_vert), - onPressed: onToggleSortOrder, - ), - ], + actions: [IconButton(icon: const Icon(Icons.swap_vert), onPressed: onToggleSortOrder)], ), body: folderState.when( data: (rootFolder) { if (folder == null) { - return FolderContent( - folder: rootFolder, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: rootFolder, root: rootFolder, sortOrder: sortOrder.value); } else { - return FolderContent( - folder: currentFolder.value!, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: currentFolder.value!, root: rootFolder, sortOrder: sortOrder.value); } }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_folder".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_folder".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_folder").tr()); }, ), @@ -130,26 +102,18 @@ class FolderContent extends HookConsumerWidget { final RootFolder root; final SortOrder sortOrder; - const FolderContent({ - super.key, - this.folder, - required this.root, - this.sortOrder = SortOrder.asc, - }); + const FolderContent({super.key, this.folder, required this.root, this.sortOrder = SortOrder.asc}); @override Widget build(BuildContext context, WidgetRef ref) { final folderRenderlist = ref.watch(folderRenderListProvider(folder!)); // Initial asset fetch - useEffect( - () { - if (folder == null) return; - ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); - return null; - }, - [folder], - ); + useEffect(() { + if (folder == null) return; + ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); + return null; + }, [folder]); if (folder == null) { return Center(child: const Text("folder_not_found").tr()); @@ -182,18 +146,12 @@ class FolderContent extends HookConsumerWidget { if (folder!.subfolders.isNotEmpty) ...folder!.subfolders.map( (subfolder) => LargeLeadingTile( - leading: Icon( - Icons.folder, - color: context.primaryColor, - size: 48, - ), + leading: Icon(Icons.folder, color: context.primaryColor, size: 48), title: Text( subfolder.name, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: subfolder.subfolders.isNotEmpty ? Text( @@ -212,23 +170,15 @@ class FolderContent extends HookConsumerWidget { onTap: () { ref.read(currentAssetProvider.notifier).set(asset); context.pushRoute( - GalleryViewerRoute( - renderList: list, - initialIndex: list.allAssets!.indexOf(asset), - ), + GalleryViewerRoute(renderList: list, initialIndex: list.allAssets!.indexOf(asset)), ); }, leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), child: SizedBox( width: 80, height: 80, - child: ThumbnailImage( - asset: asset, - showStorageIndicator: false, - ), + child: ThumbnailImage(asset: asset, showStorageIndicator: false), ), ), title: Text( @@ -236,30 +186,20 @@ class FolderContent extends HookConsumerWidget { maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( "${asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo?.fileSize ?? 0) : ""} • ${DateFormat.yMMMd().format(asset.fileCreatedAt)}", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), ], ); }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_assets".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_assets".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_assets").tr()); }, ), @@ -273,11 +213,7 @@ class FolderPath extends StatelessWidget { final RootFolder currentFolder; final RootFolder root; - const FolderPath({ - super.key, - required this.currentFolder, - required this.root, - }); + const FolderPath({super.key, required this.currentFolder, required this.root}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index f51817d067..483427d2de 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -74,17 +74,11 @@ class LibraryPage extends ConsumerWidget { const Wrap( spacing: 8, runSpacing: 8, - children: [ - PeopleCollectionCard(), - PlacesCollectionCard(), - LocalAlbumsCollectionCard(), - ], + children: [PeopleCollectionCard(), PlacesCollectionCard(), LocalAlbumsCollectionCard()], ), const SizedBox(height: 12), const QuickAccessButtons(), - const SizedBox( - height: 32, - ), + const SizedBox(height: 32), ], ), ), @@ -100,13 +94,8 @@ class QuickAccessButtons extends ConsumerWidget { return Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -130,41 +119,26 @@ class QuickAccessButtons extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( IntlKeys.folders.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( IntlKeys.locked_folder.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const LockedRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( IntlKeys.partners.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const PartnerRoute()), ), @@ -196,24 +170,13 @@ class PartnerList extends ConsumerWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), leading: userAvatar(context, partner, radius: 16), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), - ).tr( - namedArgs: { - 'user': partner.name, - }, - ), - onTap: () => context.pushRoute( - (PartnerDetailRoute(partner: partner)), - ), + style: TextStyle(fontWeight: FontWeight.w500), + ).tr(namedArgs: {'user': partner.name}), + onTap: () => context.pushRoute((PartnerDetailRoute(partner: partner))), ); }, ); @@ -241,22 +204,15 @@ class PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -308,9 +264,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - const LocalAlbumsRoute(), - ), + onTap: () => context.pushRoute(const LocalAlbumsRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -321,10 +275,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -336,10 +287,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { mainAxisSpacing: 8, physics: const NeverScrollableScrollPhysics(), children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); + return AlbumThumbnailCard(album: album, showTitle: false); }).toList(), ), ), @@ -373,11 +321,7 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - PlacesCollectionRoute( - currentLocation: null, - ), - ), + onTap: () => context.pushRoute(PlacesCollectionRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -392,10 +336,7 @@ class PlacesCollectionCard extends StatelessWidget { child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -425,12 +366,7 @@ class ActionButton extends StatelessWidget { final IconData icon; final String label; - const ActionButton({ - super.key, - required this.onPressed, - required this.icon, - required this.label, - }); + const ActionButton({super.key, required this.onPressed, required this.icon, required this.label}); @override Widget build(BuildContext context) { @@ -439,13 +375,7 @@ class ActionButton extends StatelessWidget { onPressed: onPressed, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -454,16 +384,10 @@ class ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 5c6091c76d..e52a8326df 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -18,9 +18,7 @@ class LocalAlbumsPage extends HookConsumerWidget { final albums = ref.watch(localAlbumsProvider); return Scaffold( - appBar: AppBar( - title: Text('on_this_device'.tr()), - ), + appBar: AppBar(title: Text('on_this_device'.tr())), body: ListView.builder( padding: const EdgeInsets.all(18.0), itemCount: albums.length, @@ -28,31 +26,18 @@ class LocalAlbumsPage extends HookConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(15)), - child: ImmichThumbnail( - asset: albums[index].thumbnail.value, - width: 80, - height: 80, - ), + child: ImmichThumbnail(asset: albums[index].thumbnail.value, width: 80, height: 80), ), title: Text( albums[index].name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': albums[index].assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': albums[index].assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(AlbumViewerRoute(albumId: albums[index].id)), ), diff --git a/mobile/lib/pages/library/locked/locked.page.dart b/mobile/lib/pages/library/locked/locked.page.dart index eef12a7107..aea62e0051 100644 --- a/mobile/lib/pages/library/locked/locked.page.dart +++ b/mobile/lib/pages/library/locked/locked.page.dart @@ -19,29 +19,23 @@ class LockedPage extends HookConsumerWidget { final showOverlay = useState(false); final authProviderNotifier = ref.read(authProvider.notifier); // lock the page when it is destroyed - useEffect( - () { - return () { - authProviderNotifier.lockPinCode(); - }; - }, - [], - ); + useEffect(() { + return () { + authProviderNotifier.lockPinCode(); + }; + }, []); - useEffect( - () { - if (context.mounted) { - if (appLifeCycle == AppLifecycleState.resumed) { - showOverlay.value = false; - } else { - showOverlay.value = true; - } + useEffect(() { + if (context.mounted) { + if (appLifeCycle == AppLifecycleState.resumed) { + showOverlay.value = false; + } else { + showOverlay.value = true; } + } - return null; - }, - [appLifeCycle], - ); + return null; + }, [appLifeCycle]); return Scaffold( appBar: ref.watch(multiselectProvider) ? null : const LockPageAppBar(), @@ -51,12 +45,7 @@ class LockedPage extends HookConsumerWidget { renderListProvider: lockedTimelineProvider, topWidget: Padding( padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - 'no_locked_photos_message'.tr(), - style: context.textTheme.labelLarge, - ), - ), + child: Center(child: Text('no_locked_photos_message'.tr(), style: context.textTheme.labelLarge)), ), editEnabled: false, favoriteEnabled: false, @@ -84,9 +73,7 @@ class LockPageAppBar extends ConsumerWidget implements PreferredSizeWidget { ), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'locked_folder', - ).tr(), + title: const Text('locked_folder').tr(), ); } diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index abe2247927..36befa0016 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -23,18 +23,12 @@ class PinAuthPage extends HookConsumerWidget { final isBetaTimeline = Store.isBetaTimelineEnabled; Future registerBiometric(String pinCode) async { - final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric( - context, - pinCode, - ); + final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric(context, pinCode); if (isRegistered) { context.showSnackBar( SnackBar( - content: Text( - 'biometric_auth_enabled'.tr(), - style: context.textTheme.labelLarge, - ), + content: Text('biometric_auth_enabled'.tr(), style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.primaryContainer, ), @@ -79,20 +73,14 @@ class PinAuthPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: Text('locked_folder'.tr()), - ), + appBar: AppBar(title: Text('locked_folder'.tr())), body: ListView( shrinkWrap: true, children: [ Padding( padding: const EdgeInsets.only(top: 36.0), child: showPinRegistrationForm.value - ? Center( - child: PinRegistrationForm( - onDone: () => showPinRegistrationForm.value = false, - ), - ) + ? Center(child: PinRegistrationForm(onDone: () => showPinRegistrationForm.value = false)) : Column( children: [ Center( @@ -112,17 +100,11 @@ class PinAuthPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(right: 16.0), child: TextButton.icon( - icon: const Icon( - Icons.fingerprint, - size: 28, - ), + icon: const Icon(Icons.fingerprint, size: 28), onPressed: enableBiometricAuth, label: Text( 'use_biometric'.tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - fontSize: 18, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor, fontSize: 18), ), ), ), diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index d65f2bc094..171fe0ea0d 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -22,10 +22,7 @@ class DriftPartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final potentialPartners = potentialPartnersAsync.value; if (potentialPartners == null || potentialPartners.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -77,18 +74,13 @@ class DriftPartnerPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - onPressed: potentialPartnersAsync.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: potentialPartnersAsync.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), tooltip: "add_partner".tr(), ), ], ), - body: _SharedToPartnerList( - onAddPartner: addNewUsersHandler, - onDeletePartner: onDeleteUser, - ), + body: _SharedToPartnerList(onAddPartner: addNewUsersHandler, onDeletePartner: onDeleteUser), ); } } @@ -97,10 +89,7 @@ class _SharedToPartnerList extends ConsumerWidget { final VoidCallback onAddPartner; final Function(PartnerUserDto partner) onDeletePartner; - const _SharedToPartnerList({ - required this.onAddPartner, - required this.onDeletePartner, - }); + const _SharedToPartnerList({required this.onAddPartner, required this.onDeletePartner}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -116,10 +105,7 @@ class _SharedToPartnerList extends ConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, @@ -142,18 +128,13 @@ class _SharedToPartnerList extends ConsumerWidget { leading: PartnerUserAvatar(partner: partner), title: Text(partner.name), subtitle: Text(partner.email), - trailing: IconButton( - icon: const Icon(Icons.person_remove), - onPressed: () => onDeletePartner(partner), - ), + trailing: IconButton(icon: const Icon(Icons.person_remove), onPressed: () => onDeletePartner(partner)), ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center( - child: Text("Error loading partners: $error"), - ), + error: (error, stack) => Center(child: Text("Error loading partners: $error")), ); } } diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index fb0dfe2ec3..eae4228a2d 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -22,10 +22,7 @@ class PartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final users = availableUsers.value; if (users == null || users.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -40,10 +37,7 @@ class PartnerPage extends HookConsumerWidget { onPressed: () => context.pop(u), child: Row( children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: userAvatar(context, u), - ), + Padding(padding: const EdgeInsets.only(right: 8), child: userAvatar(context, u)), Text(u.name), ], ), @@ -57,11 +51,7 @@ class PartnerPage extends HookConsumerWidget { if (ok) { ref.invalidate(partnerSharedByProvider); } else { - ImmichToast.show( - context: context, - msg: "partner_page_partner_add_failed".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "partner_page_partner_add_failed".tr(), toastType: ToastType.error); } } } @@ -87,9 +77,7 @@ class PartnerPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: Text( "partner_page_shared_to_title", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ).tr(), ), if (users.isNotEmpty) @@ -99,10 +87,7 @@ class PartnerPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: userAvatar(context, users[index]), - title: Text( - users[index].email, - style: context.textTheme.bodyLarge, - ), + title: Text(users[index].email, style: context.textTheme.bodyLarge), trailing: IconButton( icon: const Icon(Icons.person_remove), onPressed: () => onDeleteUser(users[index]), @@ -118,17 +103,12 @@ class PartnerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, child: ElevatedButton.icon( - onPressed: availableUsers.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), label: const Text("add_partner").tr(), ), diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 78af3f0939..1f15dab6a3 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -22,24 +22,18 @@ class PartnerDetailPage extends HookConsumerWidget { final inTimeline = useState(partner.inTimeline); bool toggleInProcess = false; - useEffect( - () { - Future.microtask( - () async => { - await ref.read(assetProvider.notifier).getAllAsset(), - }, - ); - return null; - }, - [], - ); + useEffect(() { + Future.microtask(() async => {await ref.read(assetProvider.notifier).getAllAsset()}); + return null; + }, []); void toggleInTimeline() async { if (toggleInProcess) return; toggleInProcess = true; try { - final ok = - await ref.read(partnerSharedWithProvider.notifier).updatePartner(partner, inTimeline: !inTimeline.value); + final ok = await ref + .read(partnerSharedWithProvider.notifier) + .updatePartner(partner, inTimeline: !inTimeline.value); if (ok) { inTimeline.value = !inTimeline.value; final action = inTimeline.value ? "shown on" : "hidden from"; @@ -65,28 +59,16 @@ class PartnerDetailPage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null - : AppBar( - title: Text(partner.name), - elevation: 0, - centerTitle: false, - ), + : AppBar(title: Text(partner.name), elevation: 0, centerTitle: false), body: MultiselectGrid( topWidget: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -96,18 +78,13 @@ class PartnerDetailPage extends HookConsumerWidget { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: inTimeline.value, - onChanged: (_) => toggleInTimeline(), - ), + trailing: Switch(value: inTimeline.value, onChanged: (_) => toggleInTimeline()), ), ), ), diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index 837553ac40..375d4d2a96 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -21,10 +21,7 @@ class PeopleCollectionPage extends HookConsumerWidget { final formFocus = useFocusNode(); final ValueNotifier search = useState(null); - showNameEditModel( - String personId, - String personName, - ) { + showNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -84,22 +81,14 @@ class PeopleCollectionPage extends HookConsumerWidget { children: [ GestureDetector( onTap: () { - context.pushRoute( - PersonResultRoute( - personId: person.id, - personName: person.name, - ), - ); + context.pushRoute(PersonResultRoute(personId: person.id, personName: person.name)); }, child: Material( shape: const CircleBorder(side: BorderSide.none), elevation: 3, child: CircleAvatar( maxRadius: isTablet ? 120 / 2 : 96 / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), @@ -115,15 +104,11 @@ class PeopleCollectionPage extends HookConsumerWidget { ), ) : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( person.name, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), ), ), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index 98bf372a96..73c38a109c 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -61,11 +61,7 @@ class PlacesCollectionPage extends HookConsumerWidget { child: MapThumbnail( onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -113,16 +109,10 @@ class PlaceTile extends StatelessWidget { SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: name, - ), + location: SearchLocationFilter(city: name), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), @@ -131,16 +121,9 @@ class PlaceTile extends StatelessWidget { return LargeLeadingTile( onTap: () => navigateToPlace(), - title: Text( - name, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(name, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: CachedNetworkImage( width: 80, height: 80, diff --git a/mobile/lib/pages/library/shared_link/shared_link.page.dart b/mobile/lib/pages/library/shared_link/shared_link.page.dart index 94af8f913b..66a77fb761 100644 --- a/mobile/lib/pages/library/shared_link/shared_link.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link.page.dart @@ -17,16 +17,13 @@ class SharedLinkPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final sharedLinks = ref.watch(sharedLinksStateProvider); - useEffect( - () { - ref.read(sharedLinksStateProvider.notifier).fetchLinks(); - return () { - if (!context.mounted) return; - ref.invalidate(sharedLinksStateProvider); - }; - }, - [], - ); + useEffect(() { + ref.read(sharedLinksStateProvider.notifier).fetchLinks(); + return () { + if (!context.mounted) return; + ref.invalidate(sharedLinksStateProvider); + }; + }, []); Widget buildNoShares() { return Column( @@ -36,30 +33,19 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: const Text( "shared_link_manage_links", - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), - child: const Text( - "you_dont_have_any_shared_links", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("you_dont_have_any_shared_links", style: TextStyle(fontSize: 14)).tr(), ), ), Expanded( child: Center( - child: Icon( - Icons.link_off, - size: 100, - color: context.themeData.iconTheme.color?.withValues(alpha: 0.5), - ), + child: Icon(Icons.link_off, size: 100, color: context.themeData.iconTheme.color?.withValues(alpha: 0.5)), ), ), ], @@ -74,9 +60,7 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 30.0), child: Text( "shared_link_manage_links", - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), - ), + style: context.textTheme.labelLarge?.copyWith(color: context.textTheme.labelLarge?.color?.withAlpha(200)), ).tr(), ), Expanded( @@ -111,11 +95,7 @@ class SharedLinkPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("shared_link_app_bar_title").tr(), - elevation: 0, - centerTitle: false, - ), + appBar: AppBar(title: const Text("shared_link_app_bar_title").tr(), elevation: 0, centerTitle: false), body: SafeArea( child: sharedLinks.widgetWhen( onError: (error, stackTrace) => buildNoShares(), diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index c6db85a0fa..dcd503335b 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -19,12 +19,7 @@ class SharedLinkEditPage extends HookConsumerWidget { final List? assetsList; final String? albumId; - const SharedLinkEditPage({ - super.key, - this.existingLink, - this.assetsList, - this.albumId, - }); + const SharedLinkEditPage({super.key, this.existingLink, this.assetsList, this.albumId}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -46,20 +41,11 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.album) { return Row( children: [ - const Text( - 'public_album', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('public_album', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Text( existingLink!.title, - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), ), ], ); @@ -68,21 +54,12 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.individual) { return Row( children: [ - const Text( - 'shared_link_individual_shared', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('shared_link_individual_shared', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: Text( existingLink!.description ?? "--", - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ), @@ -91,10 +68,7 @@ class SharedLinkEditPage extends HookConsumerWidget { } } - return const Text( - "create_link_to_share_description", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(); + return const Text("create_link_to_share_description", style: TextStyle(fontWeight: FontWeight.bold)).tr(); } Widget buildDescriptionField() { @@ -106,20 +80,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'description'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_description_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), onTapOutside: (_) => descriptionFocusNode.unfocus(), ); @@ -132,20 +98,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'password'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), ); } @@ -156,10 +114,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null, activeColor: colorScheme.primary, dense: true, - title: Text( - "show_metadata", - style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text("show_metadata", style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), ); } @@ -206,10 +161,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return DropdownMenu( label: Text( "expire_after", - style: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), ).tr(), enableSearch: false, enableFilter: false, @@ -220,26 +172,17 @@ class SharedLinkEditPage extends HookConsumerWidget { expiryAfter.value = value!; }, dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "never".tr(), - ), + DropdownMenuEntry(value: 0, label: "never".tr()), DropdownMenuEntry( value: 30, label: "shared_link_edit_expire_after_option_minutes".tr(namedArgs: {'count': "30"}), ), - DropdownMenuEntry( - value: 60, - label: "shared_link_edit_expire_after_option_hour".tr(), - ), + DropdownMenuEntry(value: 60, label: "shared_link_edit_expire_after_option_hour".tr()), DropdownMenuEntry( value: 60 * 6, label: "shared_link_edit_expire_after_option_hours".tr(namedArgs: {'count': "6"}), ), - DropdownMenuEntry( - value: 60 * 24, - label: "shared_link_edit_expire_after_option_day".tr(), - ), + DropdownMenuEntry(value: 60 * 24, label: "shared_link_edit_expire_after_option_day".tr()), DropdownMenuEntry( value: 60 * 24 * 7, label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "7"}), @@ -266,9 +209,7 @@ class SharedLinkEditPage extends HookConsumerWidget { SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -279,23 +220,14 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildNewLinkField() { return Column( children: [ - const Padding( - padding: EdgeInsets.only( - top: 20, - bottom: 20, - ), - child: Divider(), - ), + const Padding(padding: EdgeInsets.only(top: 20, bottom: 20), child: Divider()), TextFormField( readOnly: true, initialValue: newShareLink.value, decoration: InputDecoration( border: const OutlineInputBorder(), enabledBorder: themeData.inputDecorationTheme.focusedBorder, - suffixIcon: IconButton( - onPressed: copyLinkToClipboard, - icon: const Icon(Icons.copy), - ), + suffixIcon: IconButton(onPressed: copyLinkToClipboard, icon: const Icon(Icons.copy)), ), ), Padding( @@ -306,13 +238,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - child: const Text( - "done", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("done", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -325,7 +251,9 @@ class SharedLinkEditPage extends HookConsumerWidget { } Future handleNewLink() async { - final newLink = await ref.read(sharedLinkServiceProvider).createSharedLink( + final newLink = await ref + .read(sharedLinkServiceProvider) + .createSharedLink( albumId: albumId, assetIds: assetsList, showMeta: showMetadata.value, @@ -336,9 +264,7 @@ class SharedLinkEditPage extends HookConsumerWidget { expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), ); ref.invalidate(sharedLinksStateProvider); - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; @@ -390,7 +316,9 @@ class SharedLinkEditPage extends HookConsumerWidget { changeExpiry = true; } - await ref.read(sharedLinkServiceProvider).updateSharedLink( + await ref + .read(sharedLinkServiceProvider) + .updateSharedLink( existingLink!.id, showMeta: meta, allowDownload: download, @@ -406,9 +334,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - existingLink == null ? "create_link_to_share" : "edit_link", - ).tr(), + title: Text(existingLink == null ? "create_link_to_share" : "edit_link").tr(), elevation: 0, leading: const CloseButton(), centerTitle: false, @@ -416,32 +342,15 @@ class SharedLinkEditPage extends HookConsumerWidget { body: SafeArea( child: ListView( children: [ + Padding(padding: const EdgeInsets.all(padding), child: buildLinkTitle()), + Padding(padding: const EdgeInsets.all(padding), child: buildDescriptionField()), + Padding(padding: const EdgeInsets.all(padding), child: buildPasswordField()), Padding( - padding: const EdgeInsets.all(padding), - child: buildLinkTitle(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildDescriptionField(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildPasswordField(), - ), - Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildShowMetaButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildAllowDownloadButton(), ), Padding( @@ -450,48 +359,30 @@ class SharedLinkEditPage extends HookConsumerWidget { ), if (existingLink != null) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildEditExpiryButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildExpiryAfterButton(), ), if (newShareLink.value.isEmpty) Align( alignment: Alignment.bottomRight, child: Padding( - padding: const EdgeInsets.only( - right: padding + 10, - bottom: padding, - ), + padding: const EdgeInsets.only(right: padding + 10, bottom: padding), child: ElevatedButton( onPressed: existingLink != null ? handleEditLink : handleNewLink, child: Text( existingLink != null ? "shared_link_edit_submit_button" : "create_link", - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ).tr(), ), ), ), if (newShareLink.value.isNotEmpty) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildNewLinkField(), ), ], diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index 9ffe7ae704..2279998c2d 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -29,10 +29,7 @@ class TrashPage extends HookConsumerWidget { final selection = useState({}); final processing = useProcessingOverlay(); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; } @@ -43,11 +40,7 @@ class TrashPage extends HookConsumerWidget { processing.value = false; selectionEnabledHook.value = false; if (context.mounted) { - ImmichToast.show( - context: context, - msg: 'trash_emptied'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'trash_emptied'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -88,10 +81,7 @@ class TrashPage extends HookConsumerWidget { handlePermanentDelete() async { await showDialog( context: context, - builder: (context) => DeleteDialog( - alert: "delete_dialog_alert_remote", - onDelete: () => onPermanentlyDelete(), - ), + builder: (context) => DeleteDialog(alert: "delete_dialog_alert_remote", onDelete: () => onPermanentlyDelete()), ); } @@ -138,8 +128,9 @@ class TrashPage extends HookConsumerWidget { selectionEnabledHook.value = false; selection.value = {}; }, - icon: - !selectionEnabledHook.value ? const Icon(Icons.arrow_back_ios_rounded) : const Icon(Icons.close_rounded), + icon: !selectionEnabledHook.value + ? const Icon(Icons.arrow_back_ios_rounded) + : const Icon(Icons.close_rounded), ), centerTitle: !selectionEnabledHook.value, automaticallyImplyLeading: false, @@ -149,14 +140,8 @@ class TrashPage extends HookConsumerWidget { PopupMenuButton( itemBuilder: (context) { return [ - PopupMenuItem( - value: () => selectionEnabledHook.value = true, - child: const Text('select').tr(), - ), - PopupMenuItem( - value: handleEmptyTrash, - child: const Text('empty_trash').tr(), - ), + PopupMenuItem(value: () => selectionEnabledHook.value = true, child: const Text('select').tr()), + PopupMenuItem(value: handleEmptyTrash, child: const Text('empty_trash').tr()), ]; }, onSelected: (fn) => fn(), @@ -177,40 +162,28 @@ class TrashPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( selection.value.isEmpty ? 'trash_page_delete_all'.tr() : 'delete'.tr(), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleEmptyTrash - : handlePermanentDelete, + ? handleEmptyTrash + : handlePermanentDelete, ), TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), + icon: const Icon(Icons.history_rounded), label: Text( selection.value.isEmpty ? 'trash_page_restore_all'.tr() : 'restore'.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleRestoreAll - : handleRestore, + ? handleRestoreAll + : handleRestore, ), ], ), @@ -227,9 +200,7 @@ class TrashPage extends HookConsumerWidget { ), body: trashRenderList.widgetWhen( onData: (data) => data.isEmpty - ? Center( - child: Text('trash_page_no_assets'.tr()), - ) + ? Center(child: Text('trash_page_no_assets'.tr())) : Stack( children: [ SafeArea( @@ -240,13 +211,8 @@ class TrashPage extends HookConsumerWidget { showMultiSelectIndicator: false, showStack: true, topWidget: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 24, - ), - child: const Text( - "trash_page_info", - ).tr(namedArgs: {"days": "$trashDays"}), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24), + child: const Text("trash_page_info").tr(namedArgs: {"days": "$trashDays"}), ), ), ), diff --git a/mobile/lib/pages/login/change_password.page.dart b/mobile/lib/pages/login/change_password.page.dart index b05397ee38..248526df1b 100644 --- a/mobile/lib/pages/login/change_password.page.dart +++ b/mobile/lib/pages/login/change_password.page.dart @@ -9,8 +9,6 @@ class ChangePasswordPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return const Scaffold( - body: ChangePasswordForm(), - ); + return const Scaffold(body: ChangePasswordForm()); } } diff --git a/mobile/lib/pages/login/login.page.dart b/mobile/lib/pages/login/login.page.dart index 8045ae649f..e1d551900f 100644 --- a/mobile/lib/pages/login/login.page.dart +++ b/mobile/lib/pages/login/login.page.dart @@ -21,12 +21,10 @@ class LoginPage extends HookConsumerWidget { appVersion.value = packageInfo.version; } - useEffect( - () { - getAppInfo(); - return null; - }, - ); + useEffect(() { + getAppInfo(); + return null; + }); return Scaffold( body: LoginForm(), diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index 0233208585..52d4ac0125 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -26,24 +26,18 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'permission_onboarding_request', - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ).tr(), + Text('permission_onboarding_request', style: context.textTheme.titleMedium, textAlign: TextAlign.center).tr(), const SizedBox(height: 18), ElevatedButton( onPressed: () => ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission().then((permission) async { - if (permission.isGranted) { - // If permission is limited, we will show the limited - // permission page - goToBackup(); - } - }), - child: const Text( - 'continue', - ).tr(), + if (permission.isGranted) { + // If permission is limited, we will show the limited + // permission page + goToBackup(); + } + }), + child: const Text('continue').tr(), ), ], ); @@ -62,10 +56,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { textAlign: TextAlign.center, ).tr(), const SizedBox(height: 18), - ElevatedButton( - onPressed: () => goToBackup(), - child: const Text('permission_onboarding_get_started').tr(), - ), + ElevatedButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_get_started').tr()), ], ); } @@ -78,11 +69,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.yellow, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.yellow, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_limited', @@ -92,17 +79,10 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), const SizedBox(height: 8.0), - TextButton( - onPressed: () => goToBackup(), - child: const Text( - 'permission_onboarding_continue_anyway', - ).tr(), - ), + TextButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_continue_anyway').tr()), ], ); } @@ -112,11 +92,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.red, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.red, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_denied', @@ -126,9 +102,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), ], ); @@ -138,7 +112,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { PermissionStatus.limited => buildPermissionLimited(), PermissionStatus.denied => buildRequestPermission(), PermissionStatus.granted || PermissionStatus.provisional => buildPermissionGranted(), - PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied() + PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied(), }; return Scaffold( @@ -150,21 +124,13 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const ImmichLogo( - heroTag: 'logo', - ), + const ImmichLogo(heroTag: 'logo'), const ImmichTitleText(), AnimatedSwitcher( duration: const Duration(milliseconds: 500), - child: Padding( - padding: const EdgeInsets.all(18.0), - child: child, - ), - ), - TextButton( - child: const Text('back').tr(), - onPressed: () => context.maybePop(), + child: Padding(padding: const EdgeInsets.all(18.0), child: child), ), + TextButton(child: const Text('back').tr(), onPressed: () => context.maybePop()), ], ), ), diff --git a/mobile/lib/pages/photos/memory.page.dart b/mobile/lib/pages/photos/memory.page.dart index 4826e19f89..20bd32a171 100644 --- a/mobile/lib/pages/photos/memory.page.dart +++ b/mobile/lib/pages/photos/memory.page.dart @@ -16,26 +16,19 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; @RoutePage() - /// Expects [currentAssetProvider] to be set before navigating to this page class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const MemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const MemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); @@ -55,19 +48,13 @@ class MemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { @@ -94,10 +81,7 @@ class MemoryPage extends HookConsumerWidget { // Go to the next asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -109,10 +93,7 @@ class MemoryPage extends HookConsumerWidget { // Go to the previous asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -161,11 +142,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: size.width, - height: size.height, - ), + ImmichImage.imageProvider(asset: asset, width: size.width, height: size.height), context, size: size, ); @@ -219,9 +196,7 @@ class MemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -252,12 +227,7 @@ class MemoryPage extends HookConsumerWidget { return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -277,9 +247,7 @@ class MemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -290,11 +258,7 @@ class MemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: MemoryCard( - asset: asset, - title: memories[mIndex].title, - showTitle: index == 0, - ), + child: MemoryCard(asset: asset, title: memories[mIndex].title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -335,27 +299,19 @@ class MemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 2cc3c44e3a..957c32b224 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -29,17 +29,14 @@ class PhotosPage extends HookConsumerWidget { final tipOneOpacity = useState(0.0); final refreshCount = useState(0); - useEffect( - () { - ref.read(websocketProvider.notifier).connect(); - Future(() => ref.read(assetProvider.notifier).getAllAsset()); - Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); - ref.read(serverInfoProvider.notifier).getServerInfo(); + useEffect(() { + ref.read(websocketProvider.notifier).connect(); + Future(() => ref.read(assetProvider.notifier).getAllAsset()); + Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); + ref.read(serverInfoProvider.notifier).getServerInfo(); - return; - }, - [], - ); + return; + }, []); Widget buildLoadingIndicator() { Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1); @@ -53,9 +50,7 @@ class PhotosPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16.0), child: Text( 'home_page_building_timeline', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), ).tr(), ), const SizedBox(height: 8), diff --git a/mobile/lib/pages/search/all_motion_videos.page.dart b/mobile/lib/pages/search/all_motion_videos.page.dart index 5d1081db33..60bb8a6cff 100644 --- a/mobile/lib/pages/search/all_motion_videos.page.dart +++ b/mobile/lib/pages/search/all_motion_videos.page.dart @@ -17,16 +17,9 @@ class AllMotionPhotosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('search_page_motion_photos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: motionPhotos.widgetWhen( - onData: (assets) => ImmichAssetGrid( - assets: assets, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: motionPhotos.widgetWhen(onData: (assets) => ImmichAssetGrid(assets: assets)), ); } } diff --git a/mobile/lib/pages/search/all_people.page.dart b/mobile/lib/pages/search/all_people.page.dart index 6fcf07fbfa..b2814e6c13 100644 --- a/mobile/lib/pages/search/all_people.page.dart +++ b/mobile/lib/pages/search/all_people.page.dart @@ -17,13 +17,8 @@ class AllPeoplePage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'people', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + title: const Text('people').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: curatedPeople.widgetWhen( onData: (people) => ExploreGrid( diff --git a/mobile/lib/pages/search/all_places.page.dart b/mobile/lib/pages/search/all_places.page.dart index 63908a5e2a..c92f87d3ac 100644 --- a/mobile/lib/pages/search/all_places.page.dart +++ b/mobile/lib/pages/search/all_places.page.dart @@ -17,19 +17,10 @@ class AllPlacesPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'places', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: places.widgetWhen( - onData: (data) => ExploreGrid( - curatedContent: data, - ), + title: const Text('places').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: places.widgetWhen(onData: (data) => ExploreGrid(curatedContent: data)), ); } } diff --git a/mobile/lib/pages/search/all_videos.page.dart b/mobile/lib/pages/search/all_videos.page.dart index 6d123d9d7f..acad043a58 100644 --- a/mobile/lib/pages/search/all_videos.page.dart +++ b/mobile/lib/pages/search/all_videos.page.dart @@ -14,10 +14,7 @@ class AllVideosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('videos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: MultiselectGrid(renderListProvider: allVideosTimelineProvider), ); diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 62df4a50af..34522f0f04 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -62,17 +62,11 @@ class MapPage extends HookConsumerWidget { final bounds = await mapController.value!.getVisibleRegion(); final inBounds = markers.value - .where( - (m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), - ) + .where((m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude))) .toList(); // Notify bottom sheet to update asset grid only when there are new assets if (markersInBounds.value.length != inBounds.length) { - bottomSheetStreamController.add( - MapAssetsInBoundsUpdated( - inBounds.map((e) => e.assetRemoteId).toList(), - ), - ); + bottomSheetStreamController.add(MapAssetsInBoundsUpdated(inBounds.map((e) => e.assetRemoteId).toList())); } markersInBounds.value = inBounds; } @@ -80,9 +74,7 @@ class MapPage extends HookConsumerWidget { // removes all sources and layers and re-adds them with the updated markers Future reloadLayers() async { if (mapController.value != null) { - layerDebouncer.run( - () => mapController.value!.reloadAllLayersForMarkers(markers.value), - ); + layerDebouncer.run(() => mapController.value!.reloadAllLayersForMarkers(markers.value)); } } @@ -97,15 +89,12 @@ class MapPage extends HookConsumerWidget { } } - useEffect( - () { - final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); + useEffect(() { + final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); - loadMarkers(); - return currentAssetLink.close; - }, - [], - ); + loadMarkers(); + return currentAssetLink.close; + }, []); // Refetch markers when map state is changed ref.listen(mapStateNotifierProvider, (_, current) { @@ -121,16 +110,9 @@ class MapPage extends HookConsumerWidget { }); // updates the selected markers position based on the current map camera - Future updateAssetMarkerPosition( - MapMarker marker, { - bool shouldAnimate = true, - }) async { + Future updateAssetMarkerPosition(MapMarker marker, {bool shouldAnimate = true}) async { final assetPoint = await mapController.value!.toScreenLocation(marker.latLng); - selectedMarker.value = _AssetMarkerMeta( - point: assetPoint, - marker: marker, - shouldAnimate: shouldAnimate, - ); + selectedMarker.value = _AssetMarkerMeta(point: assetPoint, marker: marker, shouldAnimate: shouldAnimate); (assetPoint, marker, shouldAnimate); } @@ -160,10 +142,7 @@ class MapPage extends HookConsumerWidget { mapController.value = controller; controller.addListener(() { if (controller.isCameraMoving && selectedMarker.value != null) { - updateAssetMarkerPosition( - selectedMarker.value!.marker, - shouldAnimate: false, - ); + updateAssetMarkerPosition(selectedMarker.value!.marker, shouldAnimate: false); } }); } @@ -180,22 +159,13 @@ class MapPage extends HookConsumerWidget { } // Since we only have a single asset, we can just show GroupAssetBy.none - final renderList = await RenderList.fromAssets( - [asset], - GroupAssetsBy.none, - ); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.none); ref.read(currentAssetProvider.notifier).set(asset); if (asset.isVideo) { ref.read(showControlsProvider.notifier).show = false; } - context.pushRoute( - GalleryViewerRoute( - initialIndex: 0, - heroOffset: 0, - renderList: renderList, - ), - ); + context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList)); } /// BOTTOM SHEET CALLBACKS @@ -216,10 +186,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && assetMarker != null) { // Offset the latitude a little to show the marker just above the viewports center final offset = context.isMobile ? 0.02 : 0; - final latlng = LatLng( - assetMarker.latLng.latitude - offset, - assetMarker.latLng.longitude, - ); + final latlng = LatLng(assetMarker.latLng.latitude - offset, assetMarker.latLng.longitude); mapController.value!.animateCamera( CameraUpdate.newLatLngZoom(latlng, mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), @@ -243,10 +210,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && location != null) { mapController.value!.animateCamera( - CameraUpdate.newLatLngZoom( - LatLng(location.latitude, location.longitude), - mapZoomToAssetLevel, - ), + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); } @@ -311,9 +275,7 @@ class MapPage extends HookConsumerWidget { bottom: context.padding.bottom + 16, child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), @@ -344,11 +306,7 @@ class _AssetMarkerMeta { final MapMarker marker; final bool shouldAnimate; - const _AssetMarkerMeta({ - required this.point, - required this.marker, - required this.shouldAnimate, - }); + const _AssetMarkerMeta({required this.point, required this.marker, required this.shouldAnimate}); @override String toString() => '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index 9b7bbd0cd8..0fe8b769f5 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/map_utils.dart'; class MapLocationPickerPage extends HookConsumerWidget { final LatLng initialLatLng; - const MapLocationPickerPage({ - super.key, - this.initialLatLng = const LatLng(0, 0), - }); + const MapLocationPickerPage({super.key, this.initialLatLng = const LatLng(0, 0)}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -66,10 +63,7 @@ class MapLocationPickerPage extends HookConsumerWidget { onData: (style) => Container( clipBehavior: Clip.antiAliasWithSaveLayer, decoration: const BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(40), - bottomRight: Radius.circular(40), - ), + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40)), ), child: MapLibreMap( initialCameraPosition: CameraPosition(target: initialLatLng, zoom: 12), @@ -108,9 +102,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget { alignment: Alignment.centerLeft, child: ElevatedButton( onPressed: onClose, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ), @@ -126,11 +118,7 @@ class _BottomBar extends StatelessWidget { final Function() onUseLocation; final Function() onGetCurrentLocation; - const _BottomBar({ - required this.selectedLatLng, - required this.onUseLocation, - required this.onGetCurrentLocation, - }); + const _BottomBar({required this.selectedLatLng, required this.onUseLocation, required this.onGetCurrentLocation}); @override Widget build(BuildContext context) { @@ -149,9 +137,8 @@ class _BottomBar extends StatelessWidget { const SizedBox(width: 15), ValueListenableBuilder( valueListenable: selectedLatLng, - builder: (_, value, __) => Text( - "${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}", - ), + builder: (_, value, __) => + Text("${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}"), ), ], ), @@ -162,10 +149,7 @@ class _BottomBar extends StatelessWidget { onPressed: onUseLocation, child: const Text("map_location_picker_page_use_location").tr(), ), - ElevatedButton( - onPressed: onGetCurrentLocation, - child: const Icon(Icons.my_location), - ), + ElevatedButton(onPressed: onGetCurrentLocation, child: const Icon(Icons.my_location)), ], ), ], diff --git a/mobile/lib/pages/search/person_result.page.dart b/mobile/lib/pages/search/person_result.page.dart index 859c7e8a89..7d2e612d25 100644 --- a/mobile/lib/pages/search/person_result.page.dart +++ b/mobile/lib/pages/search/person_result.page.dart @@ -15,11 +15,7 @@ class PersonResultPage extends HookConsumerWidget { final String personId; final String personName; - const PersonResultPage({ - super.key, - required this.personId, - required this.personName, - }); + const PersonResultPage({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,10 +26,7 @@ class PersonResultPage extends HookConsumerWidget { context: context, useRootNavigator: false, builder: (BuildContext context) { - return PersonNameEditForm( - personId: personId, - personName: name.value, - ); + return PersonNameEditForm(personId: personId, personName: name.value); }, ).then((result) { if (result != null && result.success) { @@ -55,10 +48,7 @@ class PersonResultPage extends HookConsumerWidget { children: [ ListTile( leading: const Icon(Icons.edit_outlined), - title: const Text( - 'edit_name', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text('edit_name', style: TextStyle(fontWeight: FontWeight.bold)).tr(), onTap: showEditNameDialog, ), ], @@ -75,27 +65,13 @@ class PersonResultPage extends HookConsumerWidget { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'add_a_name', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), - ).tr(), - Text( - 'find_them_fast', - style: context.textTheme.labelLarge, - ).tr(), + Text('add_a_name', style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor)).tr(), + Text('find_them_fast', style: context.textTheme.labelLarge).tr(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.value, - style: context.textTheme.titleLarge, - overflow: TextOverflow.ellipsis, - ), - ], + children: [Text(name.value, style: context.textTheme.titleLarge, overflow: TextOverflow.ellipsis)], ), ); } @@ -103,16 +79,8 @@ class PersonResultPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: Text(name.value), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - actions: [ - IconButton( - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_vert_rounded), - ), - ], + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + actions: [IconButton(onPressed: buildBottomSheet, icon: const Icon(Icons.more_vert_rounded))], ), body: MultiselectGrid( renderListProvider: personAssetsProvider(personId), @@ -122,16 +90,10 @@ class PersonResultPage extends HookConsumerWidget { children: [ CircleAvatar( radius: 36, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(personId), - headers: ApiService.getRequestHeaders(), - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(personId), headers: ApiService.getRequestHeaders()), ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: buildTitleBlock(), - ), + child: Padding(padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: buildTitleBlock()), ), ], ), diff --git a/mobile/lib/pages/search/recently_taken.page.dart b/mobile/lib/pages/search/recently_taken.page.dart index cc1eb7086e..988af2faf0 100644 --- a/mobile/lib/pages/search/recently_taken.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -17,16 +17,9 @@ class RecentlyTakenPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('recently_taken_page_title').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: recents.widgetWhen( - onData: (searchResponse) => ImmichAssetGrid( - assets: searchResponse, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: recents.widgetWhen(onData: (searchResponse) => ImmichAssetGrid(assets: searchResponse)), ); } } diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index d9a20e8b4d..97205e000c 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -41,12 +41,7 @@ class SearchPage extends HookConsumerWidget { location: prefilter?.location ?? SearchLocationFilter(), camera: prefilter?.camera ?? SearchCameraFilter(), date: prefilter?.date ?? SearchDateFilter(), - display: prefilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: prefilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: prefilter?.mediaType ?? AssetType.other, language: "${context.locale.languageCode}-${context.locale.countryCode}", ), @@ -65,10 +60,7 @@ class SearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -89,9 +81,7 @@ class SearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.tr())); } previousFilter.value = filter.value; @@ -103,9 +93,7 @@ class SearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.tr())); } isSearching.value = false; @@ -113,39 +101,26 @@ class SearchPage extends HookConsumerWidget { searchPrefilter() { if (prefilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (prefilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - prefilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (prefilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(prefilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPrefilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPrefilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.tr()).join(', '), @@ -154,9 +129,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -172,10 +145,7 @@ class SearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -184,11 +154,7 @@ class SearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -204,16 +170,11 @@ class SearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -230,15 +191,10 @@ class SearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -249,10 +205,7 @@ class SearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -262,9 +215,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -280,10 +231,7 @@ class SearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -315,9 +263,7 @@ class SearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -327,13 +273,7 @@ class SearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -361,24 +301,20 @@ class SearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.tr() : assetType == AssetType.video - ? 'video'.tr() - : 'all'.tr(), + ? 'video'.tr() + : 'all'.tr(), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -390,10 +326,7 @@ class SearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.tr(), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -405,31 +338,19 @@ class SearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { filterText.add('search_filter_display_option_not_in_album'.tr()); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.tr()); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.tr()); } @@ -442,19 +363,12 @@ class SearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -467,10 +381,7 @@ class SearchPage extends HookConsumerWidget { title: 'display_options'.tr(), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -478,27 +389,15 @@ class SearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -506,10 +405,10 @@ class SearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -522,21 +421,11 @@ class SearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -610,13 +499,8 @@ class SearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -632,12 +516,7 @@ class SearchPage extends HookConsumerWidget { key: const Key('search_text_field'), controller: textSearchController, contentPadding: prefilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), - prefixIcon: prefilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + prefixIcon: prefilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -697,14 +576,9 @@ class SearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const Expanded( - child: Center(child: CircularProgressIndicator()), - ) + const Expanded(child: Center(child: CircularProgressIndicator())) else - SearchResultGrid( - onScrollEnd: loadMoreSearchResult, - isSearching: isSearching.value, - ), + SearchResultGrid(onScrollEnd: loadMoreSearchResult, isSearching: isSearching.value), ], ), ); @@ -715,11 +589,7 @@ class SearchResultGrid extends StatelessWidget { final VoidCallback onScrollEnd; final bool isSearching; - const SearchResultGrid({ - super.key, - required this.onScrollEnd, - this.isSearching = false, - }); + const SearchResultGrid({super.key, required this.onScrollEnd, this.isSearching = false}); @override Widget build(BuildContext context) { @@ -777,12 +647,7 @@ class SearchEmptyContent extends StatelessWidget { ), ), const SizedBox(height: 16), - Center( - child: Text( - 'search_page_search_photos_videos'.tr(), - style: context.textTheme.labelLarge, - ), - ), + Center(child: Text('search_page_search_photos_videos'.tr(), style: context.textTheme.labelLarge)), const SizedBox(height: 32), const QuickLinkList(), ], @@ -798,13 +663,8 @@ class QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -868,19 +728,9 @@ class QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/beta_sync_settings.page.dart index ba23ccf5eb..992557b7c6 100644 --- a/mobile/lib/pages/settings/beta_sync_settings.page.dart +++ b/mobile/lib/pages/settings/beta_sync_settings.page.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_sett @RoutePage() class BetaSyncSettingsPage extends StatelessWidget { - const BetaSyncSettingsPage({ - super.key, - }); + const BetaSyncSettingsPage({super.key}); @override Widget build(BuildContext context) { @@ -18,9 +16,7 @@ class BetaSyncSettingsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BetaSyncSettings(), diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index 2b328b7ede..9d2dbe80c2 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -60,22 +60,16 @@ class ShareIntentPage extends HookConsumerWidget { appBar: AppBar( title: Column( children: [ - const Text('upload_to_immich').tr( - namedArgs: {'count': candidates.length.toString()}, - ), + const Text('upload_to_immich').tr(namedArgs: {'count': candidates.length.toString()}), Text( currentEndpoint, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), ], ), leading: IconButton( onPressed: () { - context.navigateTo( - Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(), - ); + context.navigateTo(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); }, icon: const Icon(Icons.arrow_back), ), @@ -84,16 +78,10 @@ class ShareIntentPage extends HookConsumerWidget { itemCount: attachments.length, itemBuilder: (context, index) { final attachment = attachments[index]; - final target = candidates.firstWhere( - (element) => element.id == attachment.id, - orElse: () => attachment, - ); + final target = candidates.firstWhere((element) => element.id == attachment.id, orElse: () => attachment); return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16), child: LargeLeadingTile( onTap: () => toggleSelection(attachment), disabled: isUploaded.value, @@ -103,21 +91,11 @@ class ShareIntentPage extends HookConsumerWidget { ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: attachment.isImage - ? Image.file( - attachment.file, - width: 64, - height: 64, - fit: BoxFit.cover, - ) + ? Image.file(attachment.file, width: 64, height: 64, fit: BoxFit.cover) : const SizedBox( width: 64, height: 64, - child: Center( - child: Icon( - Icons.videocam, - color: Colors.white, - ), - ), + child: Center(child: Icon(Icons.videocam, color: Colors.white)), ), ), if (attachment.isImage) @@ -128,25 +106,13 @@ class ShareIntentPage extends HookConsumerWidget { Icons.image, color: Colors.white, size: 20, - shadows: [ - Shadow( - offset: Offset(0, 0), - blurRadius: 8.0, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 0), blurRadius: 8.0, color: Colors.black45)], ), ), ], ), - title: Text( - attachment.fileName, - style: context.textTheme.titleSmall, - ), - subtitle: Text( - attachment.fileSize, - style: context.textTheme.labelLarge, - ), + title: Text(attachment.fileName, style: context.textTheme.titleSmall), + subtitle: Text(attachment.fileSize, style: context.textTheme.labelLarge), trailing: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: UploadStatusIcon( @@ -185,22 +151,14 @@ class UploadingText extends StatelessWidget { return element.status == UploadStatus.complete; }).length; - return const Text("shared_intent_upload_button_progress_text").tr( - namedArgs: { - 'current': uploadedCount.toString(), - 'total': candidates.length.toString(), - }, - ); + return const Text( + "shared_intent_upload_button_progress_text", + ).tr(namedArgs: {'current': uploadedCount.toString(), 'total': candidates.length.toString()}); } } class UploadStatusIcon extends StatelessWidget { - const UploadStatusIcon({ - super.key, - required this.status, - required this.selected, - this.progress = 0, - }); + const UploadStatusIcon({super.key, required this.status, required this.selected, this.progress = 0}); final UploadStatus status; final double progress; @@ -218,55 +176,42 @@ class UploadStatusIcon extends StatelessWidget { final statusIcon = switch (status) { UploadStatus.enqueued => Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - semanticLabel: 'enqueued'.tr(), - ), + Icons.check_circle_rounded, + color: context.primaryColor, + semanticLabel: 'enqueued'.tr(), + ), UploadStatus.running => Stack( - alignment: AlignmentDirectional.center, - children: [ - SizedBox( - width: 40, - height: 40, - child: TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: progress), - duration: const Duration(milliseconds: 500), - builder: (context, value, _) => CircularProgressIndicator( - backgroundColor: context.colorScheme.surfaceContainerLow, - strokeWidth: 3, - value: value, - semanticsLabel: 'uploading'.tr(), - ), + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 40, + height: 40, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 500), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: context.colorScheme.surfaceContainerLow, + strokeWidth: 3, + value: value, + semanticsLabel: 'uploading'.tr(), ), ), - Text( - (progress * 100).toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ], - ), - UploadStatus.complete => Icon( - Icons.check_circle_rounded, - color: Colors.green, - semanticLabel: 'completed'.tr(), - ), - UploadStatus.notFound || UploadStatus.failed => Icon( - Icons.error_rounded, - color: Colors.red, - semanticLabel: 'failed'.tr(), - ), - UploadStatus.canceled => Icon( - Icons.cancel_rounded, - color: Colors.red, - semanticLabel: 'canceled'.tr(), - ), + ), + Text( + (progress * 100).toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ], + ), + UploadStatus.complete => Icon(Icons.check_circle_rounded, color: Colors.green, semanticLabel: 'completed'.tr()), + UploadStatus.notFound || + UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: 'failed'.tr()), + UploadStatus.canceled => Icon(Icons.cancel_rounded, color: Colors.red, semanticLabel: 'canceled'.tr()), UploadStatus.waitingToRetry || UploadStatus.paused => Icon( - Icons.pause_circle_rounded, - color: context.primaryColor, - semanticLabel: 'paused'.tr(), - ), + Icons.pause_circle_rounded, + color: context.primaryColor, + semanticLabel: 'paused'.tr(), + ), }; return statusIcon; diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index b4d1d91b69..67a320a96d 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -21,8 +21,10 @@ bool _deepEquals(Object? a, Object? b) { } if (a is Map && b is Map) { return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key])); + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } @@ -59,17 +61,7 @@ class PlatformAsset { int orientation; List _toList() { - return [ - id, - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - orientation, - ]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation]; } Object encode() { @@ -128,13 +120,7 @@ class PlatformAlbum { int assetCount; List _toList() { - return [ - id, - name, - updatedAt, - isCloud, - assetCount, - ]; + return [id, name, updatedAt, isCloud, assetCount]; } Object encode() { @@ -170,12 +156,7 @@ class PlatformAlbum { } class SyncDelta { - SyncDelta({ - required this.hasChanges, - required this.updates, - required this.deletes, - required this.assetAlbums, - }); + SyncDelta({required this.hasChanges, required this.updates, required this.deletes, required this.assetAlbums}); bool hasChanges; @@ -186,12 +167,7 @@ class SyncDelta { Map> assetAlbums; List _toList() { - return [ - hasChanges, - updates, - deletes, - assetAlbums, - ]; + return [hasChanges, updates, deletes, assetAlbums]; } Object encode() { @@ -266,8 +242,8 @@ class NativeSyncApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. NativeSyncApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 439998065c..2ab1eeaaa9 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -34,23 +34,15 @@ final _features = [ final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id); final selectedAssets = await ctx.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: assets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log( - "Selected ${selectedAssets?.length ?? 0} assets", - ); + DLog.log("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, @@ -76,11 +68,7 @@ final _features = [ icon: Icons.save_rounded, onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"), ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Clear Delta Checkpoint', icon: Icons.delete_rounded, @@ -88,10 +76,7 @@ final _features = [ ), _Feature( name: 'Clear Local Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_forever_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -102,10 +87,7 @@ final _features = [ ), _Feature( name: 'Clear Remote Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -123,29 +105,20 @@ final _features = [ ), _Feature( name: 'Local Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), _Feature( name: 'Remote Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.summarize_rounded, onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), ), _Feature( name: 'Reset Sqlite', icon: Icons.table_view_rounded, - style: const TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold), onTap: (_, ref) async { final drift = ref.read(driftProvider); // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member @@ -165,10 +138,7 @@ class FeatInDevPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Features in Development'), - centerTitle: true, - ), + appBar: AppBar(title: const Text('Features in Development'), centerTitle: true), body: Column( children: [ Flexible( @@ -178,10 +148,7 @@ class FeatInDevPage extends StatelessWidget { final feat = _features[index]; return Consumer( builder: (ctx, ref, _) => ListTile( - title: Text( - feat.name, - style: feat.style, - ), + title: Text(feat.name, style: feat.style), trailing: Icon(feat.icon), visualDensity: VisualDensity.compact, onTap: () => unawaited(feat.onTap(ctx, ref)), @@ -200,12 +167,7 @@ class FeatInDevPage extends StatelessWidget { } class _Feature { - const _Feature({ - required this.name, - required this.icon, - required this.onTap, - this.style, - }); + const _Feature({required this.name, required this.icon, required this.onTap, this.style}); final String name; final IconData icon; @@ -244,18 +206,11 @@ class _DevLogs extends StatelessWidget { return ListTile( title: Text( logMessage.message, - style: TextStyle( - color: ctx.colorScheme.onSurface, - fontSize: 14.0, - overflow: TextOverflow.ellipsis, - ), + style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle( - color: ctx.colorScheme.onSurfaceSecondary, - fontSize: 12.0, - ), + style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0), ), dense: true, visualDensity: VisualDensity.compact, diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index 70e93fd212..b48d8fc307 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -21,12 +21,7 @@ class _Summary extends StatelessWidget { final Future countFuture; final void Function()? onTap; - const _Summary({ - required this.name, - required this.countFuture, - this.leading, - this.onTap, - }); + const _Summary({required this.name, required this.countFuture, this.leading, this.onTap}); @override Widget build(BuildContext context) { @@ -40,31 +35,17 @@ class _Summary extends StatelessWidget { } else if (snapshot.hasError) { subtitle = const Icon(Icons.error_rounded); } else { - subtitle = Text( - '${snapshot.data ?? 0}', - style: ctx.textTheme.bodyLarge, - ); + subtitle = Text('${snapshot.data ?? 0}', style: ctx.textTheme.bodyLarge); } - return ListTile( - leading: leading, - title: Text(name), - trailing: subtitle, - onTap: onTap, - ); + return ListTile(leading: leading, title: Text(name), trailing: subtitle, onTap: onTap); }, ); } } final _localStats = [ - _Stat( - name: 'Local Assets', - load: (db) => db.managers.localAssetEntity.count(), - ), - _Stat( - name: 'Local Albums', - load: (db) => db.managers.localAlbumEntity.count(), - ), + _Stat(name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count()), + _Stat(name: 'Local Albums', load: (db) => db.managers.localAlbumEntity.count()), ]; @RoutePage() @@ -97,10 +78,7 @@ class LocalMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -117,15 +95,14 @@ class LocalMediaSummaryPage extends StatelessWidget { return SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; - final countFuture = - db.managers.localAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count(); + final countFuture = db.managers.localAlbumAssetEntity + .filter((f) => f.albumId.id.equals(album.id)) + .count(); return _Summary( leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - LocalTimelineRoute(album: album), - ), + onTap: () => context.router.push(LocalTimelineRoute(album: album)), ); }, itemCount: albums.length, @@ -141,38 +118,14 @@ class LocalMediaSummaryPage extends StatelessWidget { } final _remoteStats = [ - _Stat( - name: 'Remote Assets', - load: (db) => db.managers.remoteAssetEntity.count(), - ), - _Stat( - name: 'Exif Entities', - load: (db) => db.managers.remoteExifEntity.count(), - ), - _Stat( - name: 'Remote Albums', - load: (db) => db.managers.remoteAlbumEntity.count(), - ), - _Stat( - name: 'Memories', - load: (db) => db.managers.memoryEntity.count(), - ), - _Stat( - name: 'Memories Assets', - load: (db) => db.managers.memoryAssetEntity.count(), - ), - _Stat( - name: 'Stacks', - load: (db) => db.managers.stackEntity.count(), - ), - _Stat( - name: 'People', - load: (db) => db.managers.personEntity.count(), - ), - _Stat( - name: 'AssetFaces', - load: (db) => db.managers.assetFaceEntity.count(), - ), + _Stat(name: 'Remote Assets', load: (db) => db.managers.remoteAssetEntity.count()), + _Stat(name: 'Exif Entities', load: (db) => db.managers.remoteExifEntity.count()), + _Stat(name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count()), + _Stat(name: 'Memories', load: (db) => db.managers.memoryEntity.count()), + _Stat(name: 'Memories Assets', load: (db) => db.managers.memoryAssetEntity.count()), + _Stat(name: 'Stacks', load: (db) => db.managers.stackEntity.count()), + _Stat(name: 'People', load: (db) => db.managers.personEntity.count()), + _Stat(name: 'AssetFaces', load: (db) => db.managers.assetFaceEntity.count()), ]; @RoutePage() @@ -205,10 +158,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -225,15 +175,14 @@ class RemoteMediaSummaryPage extends StatelessWidget { return SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; - final countFuture = - db.managers.remoteAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count(); + final countFuture = db.managers.remoteAlbumAssetEntity + .filter((f) => f.albumId.id.equals(album.id)) + .count(); return _Summary( leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - RemoteAlbumRoute(album: album), - ), + onTap: () => context.router.push(RemoteAlbumRoute(album: album)), ); }, itemCount: albums.length, diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index e22c63ac29..0835c741ad 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -35,13 +35,8 @@ class _DriftAlbumsPageState extends ConsumerState { pinned: true, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - const DriftCreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()), ), ], showUploadButton: false, @@ -49,9 +44,7 @@ class _DriftAlbumsPageState extends ConsumerState { AlbumSelector( onAlbumSelected: (album) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); + context.router.push(RemoteAlbumRoute(album: album)); }, ), ], diff --git a/mobile/lib/presentation/pages/drift_archive.page.dart b/mobile/lib/presentation/pages/drift_archive.page.dart index 8a6f1607d0..ee8417bcad 100644 --- a/mobile/lib/presentation/pages/drift_archive.page.dart +++ b/mobile/lib/presentation/pages/drift_archive.page.dart @@ -16,18 +16,16 @@ class DriftArchivePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access archive'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } - final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart index 0e5631082b..19f813cdb5 100644 --- a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class DriftAssetSelectionTimelinePage extends ConsumerWidget { final Set lockedSelectionAssets; - const DriftAssetSelectionTimelinePage({ - super.key, - this.lockedSelectionAssets = const {}, - }); + const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,27 +18,19 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget { overrides: [ multiSelectProvider.overrideWith( () => MultiSelectNotifier( - MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: lockedSelectionAssets, - forceEnable: true, - ), + MultiSelectState(selectedAssets: {}, lockedSelectionAssets: lockedSelectionAssets, forceEnable: true), ), ), - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access asset selection timeline', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access asset selection timeline'); + } - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: const Timeline(), ); diff --git a/mobile/lib/presentation/pages/drift_create_album.page.dart b/mobile/lib/presentation/pages/drift_create_album.page.dart index ba7157b15d..bac23b45c7 100644 --- a/mobile/lib/presentation/pages/drift_create_album.page.dart +++ b/mobile/lib/presentation/pages/drift_create_album.page.dart @@ -70,12 +70,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildContent() { if (selectedAssets.isEmpty) { - return SliverList( - delegate: SliverChildListDelegate([ - _buildEmptyState(), - _buildSelectPhotosButton(), - ]), - ); + return SliverList(delegate: SliverChildListDelegate([_buildEmptyState(), _buildSelectPhotosButton()])); } else { return _buildSelectedImageGrid(); } @@ -84,10 +79,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildEmptyState() { return Padding( padding: const EdgeInsets.only(top: 0, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).t(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).t(), ); } @@ -97,27 +89,17 @@ class _DriftCreateAlbumPageState extends ConsumerState { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric( - vertical: 24.0, - horizontal: 16.0, - ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)), - ), + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotos, icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( - padding: const EdgeInsets.only( - left: 8.0, - ), + padding: const EdgeInsets.only(left: 8.0), child: Text( 'create_shared_album_page_share_select_photos', - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).t(), ), ), @@ -133,16 +115,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { crossAxisSpacing: 1.0, mainAxisSpacing: 1.0, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final asset = selectedAssets.elementAt(index); - return GestureDetector( - onTap: onBackgroundTapped, - child: Thumbnail(asset: asset), - ); - }, - childCount: selectedAssets.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final asset = selectedAssets.elementAt(index); + return GestureDetector( + onTap: onBackgroundTapped, + child: Thumbnail(asset: asset), + ); + }, childCount: selectedAssets.length), ), ); } @@ -162,9 +141,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Future onSelectPhotos() async { final assets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: selectedAssets, - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets), ); if (assets == null || assets.isEmpty) { @@ -183,16 +160,15 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (title.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('create_album_title_required'.t()), - backgroundColor: context.colorScheme.error, - ), + SnackBar(content: Text('create_album_title_required'.t()), backgroundColor: context.colorScheme.error), ); } return; } - final album = await ref.watch(remoteAlbumProvider.notifier).createAlbum( + final album = await ref + .watch(remoteAlbumProvider.notifier) + .createAlbum( title: title, description: albumDescriptionController.text.trim(), assetIds: selectedAssets.map((asset) { @@ -203,18 +179,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (album != null) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.replaceRoute( - RemoteAlbumRoute(album: album), - ); + context.replaceRoute(RemoteAlbumRoute(album: album)); } } Widget buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0), child: _AlbumTitleTextField( focusNode: albumTitleTextFieldFocusNode, textController: albumTitleController, @@ -230,11 +201,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - top: 8, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0, top: 8), child: _AlbumViewerEditableDescription( textController: albumDescriptionController, focusNode: albumDescriptionTextFieldFocusNode, @@ -244,11 +211,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildControlButton() { return Padding( - padding: const EdgeInsets.only( - left: 12.0, - top: 8.0, - bottom: 8.0, - ), + padding: const EdgeInsets.only(left: 12.0, top: 8.0, bottom: 8.0), child: SizedBox( height: 42.0, child: ListView( @@ -272,10 +235,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { elevation: 0, centerTitle: false, backgroundColor: context.scaffoldBackgroundColor, - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.close_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.close_rounded)), title: const Text('create_album').t(), actions: [ TextButton( @@ -292,12 +252,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { ), body: GestureDetector( onTap: onBackgroundTapped, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildContent(), - ], - ), + child: CustomScrollView(slivers: [_buildSliverAppBar(), _buildContent()]), ), ); } @@ -341,11 +296,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { Widget build(BuildContext context) { return TextField( focusNode: widget.focusNode, - style: TextStyle( - fontSize: 28.0, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28.0, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: widget.textController, onTap: () { if (widget.textController.text == 'create_album_page_untitled'.t(context: context)) { @@ -353,35 +304,23 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), suffixIcon: widget.textController.text.isNotEmpty && widget.isFocus ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(16.0), - ), + borderRadius: BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintText: 'add_a_title'.t(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( @@ -398,10 +337,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } class _AlbumViewerEditableDescription extends StatefulWidget { - const _AlbumViewerEditableDescription({ - required this.textController, - required this.focusNode, - }); + const _AlbumViewerEditableDescription({required this.textController, required this.focusNode}); final TextEditingController textController; final FocusNode focusNode; @@ -448,37 +384,23 @@ class _AlbumViewerEditableDescriptionState extends State<_AlbumViewerEditableDes minLines: 1, controller: widget.textController, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0), suffixIcon: widget.focusNode.hasFocus && widget.textController.text.isNotEmpty ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( fontSize: 16.0, diff --git a/mobile/lib/presentation/pages/drift_favorite.page.dart b/mobile/lib/presentation/pages/drift_favorite.page.dart index 5648fd7fac..2bc3f26363 100644 --- a/mobile/lib/presentation/pages/drift_favorite.page.dart +++ b/mobile/lib/presentation/pages/drift_favorite.page.dart @@ -16,18 +16,16 @@ class DriftFavoritePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access favorite'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access favorite'); + } - final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 5cf47bc995..28fa6c6a16 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -27,12 +27,7 @@ class DriftLibraryPage extends ConsumerWidget { return const Scaffold( body: CustomScrollView( slivers: [ - ImmichSliverAppBar( - snap: false, - floating: false, - pinned: true, - showUploadButton: false, - ), + ImmichSliverAppBar(snap: false, floating: false, pinned: true, showUploadButton: false), _ActionButtonGrid(), _CollectionCards(), _QuickAccessButtonList(), @@ -47,9 +42,7 @@ class _ActionButtonGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12), @@ -97,11 +90,7 @@ class _ActionButtonGrid extends ConsumerWidget { } class _ActionButton extends StatelessWidget { - const _ActionButton({ - required this.icon, - required this.onTap, - required this.label, - }); + const _ActionButton({required this.icon, required this.onTap, required this.label}); final IconData icon; final VoidCallback onTap; @@ -114,13 +103,7 @@ class _ActionButton extends StatelessWidget { onPressed: onTap, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -129,16 +112,10 @@ class _ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } @@ -155,11 +132,7 @@ class _CollectionCards extends StatelessWidget { child: Wrap( spacing: 8, runSpacing: 8, - children: [ - _PeopleCollectionCard(), - _PlacesCollectionCard(), - _LocalAlbumsCollectionCard(), - ], + children: [_PeopleCollectionCard(), _PlacesCollectionCard(), _LocalAlbumsCollectionCard()], ), ), ); @@ -188,22 +161,15 @@ class _PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -253,9 +219,7 @@ class _PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - DriftPlaceRoute(currentLocation: null), - ), + onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -270,10 +234,7 @@ class _PlacesCollectionCard extends StatelessWidget { child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -323,10 +284,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -340,24 +298,14 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { children: albums.when( data: (data) { return data.take(4).map((album) { - return LocalAlbumThumbnail( - albumId: album.id, - ); + return LocalAlbumThumbnail(albumId: album.id); }).toList(); }, error: (error, _) { - return [ - Center( - child: Text('Error: $error'), - ), - ]; + return [Center(child: Text('Error: $error'))]; }, loading: () { - return [ - const Center( - child: CircularProgressIndicator(), - ), - ]; + return [const Center(child: CircularProgressIndicator())]; }, ), ), @@ -394,13 +342,8 @@ class _QuickAccessButtonList extends ConsumerWidget { sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -425,41 +368,26 @@ class _QuickAccessButtonList extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( 'folders'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( 'locked_folder'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftLockedFolderRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( 'partners'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftPartnerRoute()), ), @@ -494,22 +422,13 @@ class _PartnerList extends StatelessWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), - leading: PartnerUserAvatar( - partner: partner, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), + leading: PartnerUserAvatar(partner: partner), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontWeight: FontWeight.w500), ).t(context: context, args: {'user': partner.name}), - onTap: () => context.pushRoute( - DriftPartnerDetailRoute(partner: partner), - ), + onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)), ); }, ); diff --git a/mobile/lib/presentation/pages/drift_local_album.page.dart b/mobile/lib/presentation/pages/drift_local_album.page.dart index 7add23259e..536d9d82e2 100644 --- a/mobile/lib/presentation/pages/drift_local_album.page.dart +++ b/mobile/lib/presentation/pages/drift_local_album.page.dart @@ -16,14 +16,7 @@ class DriftLocalAlbumsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: CustomScrollView( - slivers: [ - LocalAlbumsSliverAppBar(), - _AlbumList(), - ], - ), - ); + return const Scaffold(body: CustomScrollView(slivers: [LocalAlbumsSliverAppBar(), _AlbumList()])); } } @@ -37,10 +30,7 @@ class _AlbumList extends ConsumerWidget { return albums.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -49,9 +39,7 @@ class _AlbumList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading albums: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -60,10 +48,7 @@ class _AlbumList extends ConsumerWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -77,30 +62,12 @@ class _AlbumList extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: SizedBox( - width: 80, - height: 80, - child: LocalAlbumThumbnail( - albumId: album.id, - ), - ), - title: Text( - album.name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), + leadingPadding: const EdgeInsets.only(right: 16), + leading: SizedBox(width: 80, height: 80, child: LocalAlbumThumbnail(albumId: album.id)), + title: Text(album.name, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': album.assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(LocalTimelineRoute(album: album)), ), diff --git a/mobile/lib/presentation/pages/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/drift_locked_folder.page.dart index 417e902de3..b19e8468ca 100644 --- a/mobile/lib/presentation/pages/drift_locked_folder.page.dart +++ b/mobile/lib/presentation/pages/drift_locked_folder.page.dart @@ -45,27 +45,23 @@ class _DriftLockedFolderPageState extends ConsumerState w Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access locked folder'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access locked folder'); + } - final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: _showOverlay ? const SizedBox() : PopScope( onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null, child: Timeline( - appBar: MesmerizingSliverAppBar( - title: 'locked_folder'.t(context: context), - ), + appBar: MesmerizingSliverAppBar(title: 'locked_folder'.t(context: context)), bottomSheet: const LockedFolderBottomSheet(), ), ), diff --git a/mobile/lib/presentation/pages/drift_memory.page.dart b/mobile/lib/presentation/pages/drift_memory.page.dart index c42feff0ea..55e5d24ecb 100644 --- a/mobile/lib/presentation/pages/drift_memory.page.dart +++ b/mobile/lib/presentation/pages/drift_memory.page.dart @@ -22,20 +22,14 @@ class DriftMemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const DriftMemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); @@ -55,19 +49,13 @@ class DriftMemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { @@ -94,10 +82,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Go to the next asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -109,10 +94,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Go to the previous asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -160,14 +142,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); - await precacheImage( - getFullImageProvider( - asset, - size: Size(size.width, size.height), - ), - context, - size: size, - ); + await precacheImage(getFullImageProvider(asset, size: Size(size.width, size.height)), context, size: size); } // Precache the next page right away if we are on the first page @@ -219,9 +194,7 @@ class DriftMemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -249,23 +222,13 @@ class DriftMemoryPage extends HookConsumerWidget { } final yearsAgo = DateTime.now().year - memories[mIndex].data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); // Build horizontal page final assetController = memoryAssetPageControllers[mIndex]; return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -285,9 +248,7 @@ class DriftMemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -298,11 +259,7 @@ class DriftMemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: DriftMemoryCard( - asset: asset, - title: title, - showTitle: index == 0, - ), + child: DriftMemoryCard(asset: asset, title: title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -343,35 +300,24 @@ class DriftMemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), ), - DriftMemoryBottomInfo( - memory: memories[mIndex], - title: title, - ), + DriftMemoryBottomInfo(memory: memories[mIndex], title: title), ], ); }, diff --git a/mobile/lib/presentation/pages/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/drift_partner_detail.page.dart index 3d9d28aeab..95c5b008b3 100644 --- a/mobile/lib/presentation/pages/drift_partner_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_partner_detail.page.dart @@ -15,28 +15,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPartnerDetailPage extends StatelessWidget { final PartnerUserDto partner; - const DriftPartnerDetailPage({ - super.key, - required this.partner, - }); + const DriftPartnerDetailPage({super.key, required this.partner}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: partner.name, - icon: Icons.person_outline, - ), + appBar: MesmerizingSliverAppBar(title: partner.name, icon: Icons.person_outline), topSliverWidget: _InfoBox(partner: partner), topSliverWidgetHeight: 110, bottomSheet: const PartnerDetailBottomSheet(), @@ -48,9 +40,7 @@ class DriftPartnerDetailPage extends StatelessWidget { class _InfoBox extends ConsumerStatefulWidget { final PartnerUserDto partner; - const _InfoBox({ - required this.partner, - }); + const _InfoBox({required this.partner}); @override ConsumerState<_InfoBox> createState() => _InfoBoxState(); @@ -72,10 +62,7 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { } try { - await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline( - widget.partner.id, - user.id, - ); + await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(widget.partner.id, user.id); setState(() { _inTimeline = !_inTimeline; @@ -101,18 +88,10 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -122,18 +101,13 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: _inTimeline, - onChanged: (_) => _toggleInTimeline(), - ), + trailing: Switch(value: _inTimeline, onChanged: (_) => _toggleInTimeline()), ), ), ), diff --git a/mobile/lib/presentation/pages/drift_place.page.dart b/mobile/lib/presentation/pages/drift_place.page.dart index 969bfc70fd..e85bb90d54 100644 --- a/mobile/lib/presentation/pages/drift_place.page.dart +++ b/mobile/lib/presentation/pages/drift_place.page.dart @@ -52,9 +52,7 @@ class _PlaceSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: search.value == null, centerTitle: true, title: search.value != null @@ -98,20 +96,14 @@ class _Map extends StatelessWidget { child: MapThumbnail( onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), ) - : const SliverToBoxAdapter( - child: SizedBox.shrink(), - ); + : const SliverToBoxAdapter(child: SizedBox.shrink()); } } @@ -127,10 +119,7 @@ class _PlaceList extends ConsumerWidget { return places.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -139,9 +128,7 @@ class _PlaceList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading places: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -174,21 +161,10 @@ class _PlaceTile extends StatelessWidget { Widget build(BuildContext context) { return LargeLeadingTile( onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)), - title: Text( - place.$1, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: Thumbnail( - size: const Size(80, 80), - fit: BoxFit.cover, - remoteId: place.$2, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: Thumbnail(size: const Size(80, 80), fit: BoxFit.cover, remoteId: place.$2), ), ); } diff --git a/mobile/lib/presentation/pages/drift_place_detail.page.dart b/mobile/lib/presentation/pages/drift_place_detail.page.dart index d55725231f..0f50227945 100644 --- a/mobile/lib/presentation/pages/drift_place_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_place_detail.page.dart @@ -9,28 +9,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPlaceDetailPage extends StatelessWidget { final String place; - const DriftPlaceDetailPage({ - super.key, - required this.place, - }); + const DriftPlaceDetailPage({super.key, required this.place}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).place(place); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).place(place); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: place, - icon: Icons.location_on, - ), + appBar: MesmerizingSliverAppBar(title: place, icon: Icons.location_on), ), ); } diff --git a/mobile/lib/presentation/pages/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/drift_recently_taken.page.dart index c99c36bd24..ceb121b124 100644 --- a/mobile/lib/presentation/pages/drift_recently_taken.page.dart +++ b/mobile/lib/presentation/pages/drift_recently_taken.page.dart @@ -15,24 +15,18 @@ class DriftRecentlyTakenPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access recently taken', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access recently taken'); + } - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())), ); } } diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 4173e262bc..336050d5a9 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -21,10 +21,7 @@ import 'package:immich_mobile/widgets/common/remote_album_sliver_app_bar.dart'; class RemoteAlbumPage extends ConsumerStatefulWidget { final RemoteAlbum album; - const RemoteAlbumPage({ - super.key, - required this.album, - }); + const RemoteAlbumPage({super.key, required this.album}); @override ConsumerState createState() => _RemoteAlbumPageState(); @@ -40,16 +37,16 @@ class _RemoteAlbumPageState extends ConsumerState { final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); final newAssets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: albumAssets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), ); if (newAssets == null || newAssets.isEmpty) { return; } - final added = await ref.read(remoteAlbumProvider.notifier).addAssets( + final added = await ref + .read(remoteAlbumProvider.notifier) + .addAssets( widget.album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; @@ -60,21 +57,14 @@ class _RemoteAlbumPageState extends ConsumerState { if (added > 0) { ImmichToast.show( context: context, - msg: "assets_added_to_album_count".t( - context: context, - args: { - 'count': added.toString(), - }, - ), + msg: "assets_added_to_album_count".t(context: context, args: {'count': added.toString()}), toastType: ToastType.success, ); } } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>( - DriftUserSelectionRoute(album: widget.album), - ); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: widget.album)); if (newUsers == null || newUsers.isEmpty) { return; @@ -86,12 +76,7 @@ class _RemoteAlbumPageState extends ConsumerState { if (newUsers.isNotEmpty) { ImmichToast.show( context: context, - msg: "users_added_to_album_count".t( - context: context, - args: { - 'count': newUsers.length, - }, - ), + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), toastType: ToastType.success, ); } @@ -107,9 +92,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder( - widget.album.id, - ); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(widget.album.id); ref.invalidate(timelineServiceProvider); } @@ -123,16 +106,9 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - 'album_delete_confirmation'.t( - context: context, - args: {'album': widget.album.name}, - ), - ), + Text('album_delete_confirmation'.t(context: context, args: {'album': widget.album.name})), const SizedBox(height: 8), - Text( - 'album_delete_confirmation_description'.t(context: context), - ), + Text('album_delete_confirmation_description'.t(context: context)), ], ), actions: [ @@ -142,9 +118,7 @@ class _RemoteAlbumPageState extends ConsumerState { ), TextButton( onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - ), + style: TextButton.styleFrom(foregroundColor: Theme.of(context).colorScheme.error), child: Text('delete_album'.t(context: context)), ), ], @@ -230,13 +204,11 @@ class _RemoteAlbumPageState extends ConsumerState { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: RemoteAlbumSliverAppBar( @@ -245,9 +217,7 @@ class _RemoteAlbumPageState extends ConsumerState { onToggleAlbumOrder: () => toggleAlbumOrder(), onEditTitle: () => showEditTitleAndDescription(context), ), - bottomSheet: RemoteAlbumBottomSheet( - album: widget.album, - ), + bottomSheet: RemoteAlbumBottomSheet(album: widget.album), ), ); } @@ -257,18 +227,13 @@ class _EditAlbumData { final String name; final String? description; - const _EditAlbumData({ - required this.name, - this.description, - }); + const _EditAlbumData({required this.name, this.description}); } class _EditAlbumDialog extends ConsumerStatefulWidget { final RemoteAlbum album; - const _EditAlbumDialog({ - required this.album, - }); + const _EditAlbumDialog({required this.album}); @override ConsumerState<_EditAlbumDialog> createState() => _EditAlbumDialogState(); @@ -302,19 +267,14 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { final newTitle = titleController.text.trim(); final newDescription = descriptionController.text.trim(); - await ref.read(remoteAlbumProvider.notifier).updateAlbum( - widget.album.id, - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ); + await ref + .read(remoteAlbumProvider.notifier) + .updateAlbum(widget.album.id, name: newTitle, description: newDescription.isEmpty ? null : newDescription); if (mounted) { - Navigator.of(context).pop( - _EditAlbumData( - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ), - ); + Navigator.of( + context, + ).pop(_EditAlbumData(name: newTitle, description: newDescription.isEmpty ? null : newDescription)); } } catch (e) { if (mounted) { @@ -331,11 +291,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { Widget build(BuildContext context) { return Dialog( insetPadding: const EdgeInsets.all(24), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16), @@ -348,16 +304,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { children: [ Row( children: [ - Icon( - Icons.edit_outlined, - color: context.colorScheme.primary, - size: 24, - ), + Icon(Icons.edit_outlined, color: context.colorScheme.primary, size: 24), const SizedBox(width: 12), - Text( - 'edit_album'.t(context: context), - style: context.textTheme.titleMedium, - ), + Text('edit_album'.t(context: context), style: context.textTheme.titleMedium), ], ), const SizedBox(height: 24), @@ -365,9 +314,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Album Name Text( 'album_name'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -375,9 +322,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 1, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), @@ -394,9 +339,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Description Text( 'description'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -404,11 +347,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 4, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(12), - ), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 61fc5e35f7..4d18d12d01 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -16,18 +16,16 @@ class DriftTrashPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access trash'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access trash'); + } - final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( showStorageIndicator: true, @@ -42,18 +40,14 @@ class DriftTrashPage extends StatelessWidget { topSliverWidgetHeight: 24, topSliverWidget: Consumer( builder: (context, ref, child) { - final trashDays = ref.watch( - serverInfoProvider.select((v) => v.serverConfig.trashDays), - ); + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); return SliverPadding( padding: const EdgeInsets.all(16.0), sliver: SliverToBoxAdapter( child: SizedBox( height: 24.0, - child: const Text( - "trash_page_info", - ).t(context: context, args: {"days": "$trashDays"}), + child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), ), ), ); diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index 5aaa438a11..e8835e7146 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -49,10 +49,7 @@ final driftUsersProvider = FutureProvider.autoDispose>((ref) async class DriftUserSelectionPage extends HookConsumerWidget { final RemoteAlbum album; - const DriftUserSelectionPage({ - super.key, - required this.album, - }); + const DriftUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -65,17 +62,9 @@ class DriftUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -88,31 +77,19 @@ class DriftUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -122,31 +99,15 @@ class DriftUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -159,9 +120,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -173,10 +132,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { actions: [ TextButton( onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), diff --git a/mobile/lib/presentation/pages/drift_video.page.dart b/mobile/lib/presentation/pages/drift_video.page.dart index 94c5620f9a..eef05acdce 100644 --- a/mobile/lib/presentation/pages/drift_video.page.dart +++ b/mobile/lib/presentation/pages/drift_video.page.dart @@ -15,22 +15,18 @@ class DriftVideoPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to video'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to video'); + } - final timelineService = ref.watch(timelineFactoryProvider).video(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).video(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'videos'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())), ); } } diff --git a/mobile/lib/presentation/pages/local_timeline.page.dart b/mobile/lib/presentation/pages/local_timeline.page.dart index d322b5d9d2..67bc17cb37 100644 --- a/mobile/lib/presentation/pages/local_timeline.page.dart +++ b/mobile/lib/presentation/pages/local_timeline.page.dart @@ -17,13 +17,11 @@ class LocalTimelinePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar(title: album.name), diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 868f1ff298..f61fad5484 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -44,12 +44,7 @@ class DriftSearchPage extends HookConsumerWidget { location: preFilter?.location ?? SearchLocationFilter(), camera: preFilter?.camera ?? SearchCameraFilter(), date: preFilter?.date ?? SearchDateFilter(), - display: preFilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: preFilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: preFilter?.mediaType ?? AssetType.other, language: "${context.locale.languageCode}-${context.locale.countryCode}", ), @@ -68,10 +63,7 @@ class DriftSearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -92,9 +84,7 @@ class DriftSearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context))); } previousFilter.value = filter.value; @@ -106,9 +96,7 @@ class DriftSearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.t(context: context))); } isSearching.value = false; @@ -116,39 +104,26 @@ class DriftSearchPage extends HookConsumerWidget { searchPreFilter() { if (preFilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (preFilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - preFilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (preFilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(preFilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPreFilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPreFilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', '), @@ -157,9 +132,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -175,10 +148,7 @@ class DriftSearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -187,11 +157,7 @@ class DriftSearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -207,16 +173,11 @@ class DriftSearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -233,15 +194,10 @@ class DriftSearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -252,10 +208,7 @@ class DriftSearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -265,9 +218,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -283,10 +234,7 @@ class DriftSearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -318,9 +266,7 @@ class DriftSearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -330,13 +276,7 @@ class DriftSearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -365,24 +305,20 @@ class DriftSearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.t(context: context) : assetType == AssetType.video - ? 'video'.t(context: context) - : 'all'.t(context: context), + ? 'video'.t(context: context) + : 'all'.t(context: context), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -394,10 +330,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.t(context: context), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -409,33 +342,19 @@ class DriftSearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText.add( - 'search_filter_display_option_not_in_album'.t(context: context), - ); + filterText.add('search_filter_display_option_not_in_album'.t(context: context)); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.t(context: context)); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.t(context: context)); } @@ -448,19 +367,12 @@ class DriftSearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -473,10 +385,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'display_options'.t(context: context), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -484,27 +393,15 @@ class DriftSearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -512,10 +409,10 @@ class DriftSearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -528,21 +425,11 @@ class DriftSearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -616,13 +503,8 @@ class DriftSearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -638,12 +520,7 @@ class DriftSearchPage extends HookConsumerWidget { key: const Key('search_text_field'), controller: textSearchController, contentPadding: preFilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), - prefixIcon: preFilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + prefixIcon: preFilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -705,10 +582,7 @@ class DriftSearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const SliverFillRemaining( - hasScrollBody: false, - child: Center(child: CircularProgressIndicator()), - ) + const SliverFillRemaining(hasScrollBody: false, child: Center(child: CircularProgressIndicator())) else _SearchResultGrid(onScrollEnd: loadMoreSearchResult), ], @@ -747,19 +621,13 @@ class _SearchResultGrid extends ConsumerWidget { child: SliverFillRemaining( child: ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - key: ValueKey(searchResult.totalAssets), - appBar: null, - groupBy: GroupAssetsBy.none, - ), + child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none), ), ), ); @@ -784,16 +652,10 @@ class _SearchEmptyContent extends StatelessWidget { ), const SizedBox(height: 16), Center( - child: Text( - 'search_page_search_photos_videos'.t(context: context), - style: context.textTheme.labelLarge, - ), + child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge), ), const SizedBox(height: 32), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: _QuickLinkList(), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()), ], ), ); @@ -807,13 +669,8 @@ class _QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -876,19 +733,9 @@ class _QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/presentation/pages/search/paginated_search.provider.dart b/mobile/lib/presentation/pages/search/paginated_search.provider.dart index c93d002b95..718a241ba4 100644 --- a/mobile/lib/presentation/pages/search/paginated_search.provider.dart +++ b/mobile/lib/presentation/pages/search/paginated_search.provider.dart @@ -24,10 +24,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index e461a9028e..d30ba07d0c 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -27,10 +27,7 @@ class ArchiveActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'archive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'archive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart index 9704c4b13b..5ec6c8bc54 100644 --- a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart @@ -39,14 +39,10 @@ class BaseActionButton extends StatelessWidget { } return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), child: MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), textColor: textColor, onPressed: onPressed, onLongPress: onLongPressed, @@ -59,10 +55,7 @@ class BaseActionButton extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - ), + style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400), maxLines: 3, textAlign: TextAlign.center, softWrap: true, diff --git a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart index c80dbaaf2d..26b8ba6f47 100644 --- a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart @@ -20,10 +20,7 @@ class CastActionButton extends ConsumerWidget { iconColor: isCasting ? context.primaryColor : null, // null = default color label: "cast".t(context: context), onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, menuItem: menuItem, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart index f910a2a9e2..723700af55 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart @@ -40,9 +40,7 @@ class DeleteActionButton extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(true), child: Text( 'confirm'.t(context: context), - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ], @@ -58,10 +56,7 @@ class DeleteActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'delete_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'delete_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 7a9465dfb6..cccdee9b3a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -33,10 +33,7 @@ class DeleteLocalActionButton extends ConsumerWidget { return; } - final successMessage = 'delete_local_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'delete_local_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart index dafbdbc78e..cb0e7091c8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -43,17 +43,10 @@ class DeleteTrashActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( "delete".t(context: context), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart index fc642483be..1a8a1a5c39 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart @@ -25,10 +25,7 @@ class EditLocationActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'edit_location_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'edit_location_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart index c330a7bbb1..0aca5158ef 100644 --- a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart @@ -12,11 +12,7 @@ class FavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const FavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const FavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,10 +27,7 @@ class FavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'favorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'favorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart index 8857a1b2d9..a1f6f7e7d1 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -12,11 +12,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { final String albumId; final ActionSource source; - const RemoveFromAlbumActionButton({ - super.key, - required this.albumId, - required this.source, - }); + const RemoveFromAlbumActionButton({super.key, required this.albumId, required this.source}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart index 7cdc28e1e8..e7928bd325 100644 --- a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -20,10 +20,7 @@ class RestoreTrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).restoreTrash(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'assets_restored_count'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'assets_restored_count'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( @@ -38,16 +35,8 @@ class RestoreTrashActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), - label: Text( - 'restore'.t(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + icon: const Icon(Icons.history_rounded), + label: Text('restore'.t(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart index d448c5ce86..22fccf5473 100644 --- a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart @@ -27,10 +27,7 @@ class StackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).stack(user.id, source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'stack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'stack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index d26bdfad04..df8f544601 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -30,10 +30,7 @@ class TrashActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'trash_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'trash_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart index d01a5cc47b..b457a1b4ca 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart @@ -21,10 +21,7 @@ class UnArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unArchive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unarchive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unarchive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart index a45bdfb06a..7fdc5e81e8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart @@ -12,11 +12,7 @@ class UnFavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const UnFavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const UnFavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,10 +27,7 @@ class UnFavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unfavorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unfavorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart index bf96e6ea41..ecc8a39c74 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart @@ -21,10 +21,7 @@ class UnStackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unStack(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unstack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unstack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 9e2fc9b309..f037d365d8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -20,10 +20,7 @@ class UploadActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).upload(source); - final successMessage = 'upload_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'upload_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 5d9378ecaf..cb6a38041d 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -27,10 +27,7 @@ typedef AlbumSelectorCallback = void Function(RemoteAlbum album); class AlbumSelector extends ConsumerStatefulWidget { final AlbumSelectorCallback onAlbumSelected; - const AlbumSelector({ - super.key, - required this.onAlbumSelected, - }); + const AlbumSelector({super.key, required this.onAlbumSelected}); @override ConsumerState createState() => _AlbumSelectorState(); @@ -113,21 +110,10 @@ class _AlbumSelectorState extends ConsumerState { onSearch: onSearch, searchController: searchController, ), - _QuickSortAndViewMode( - isGrid: isGrid, - onToggleViewMode: toggleViewMode, - ), + _QuickSortAndViewMode(isGrid: isGrid, onToggleViewMode: toggleViewMode), isGrid - ? _AlbumGrid( - albums: albums, - userId: userId, - onAlbumSelected: widget.onAlbumSelected, - ) - : _AlbumList( - albums: albums, - userId: userId, - onAlbumSelected: widget.onAlbumSelected, - ), + ? _AlbumGrid(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected) + : _AlbumList(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected), ], ); } @@ -151,18 +137,12 @@ class _SortButtonState extends ConsumerState<_SortButton> { setState(() { albumSortIsReverse = !albumSortIsReverse; }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); } else { setState(() { albumSortOption = sortMode; }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); } } @@ -172,15 +152,9 @@ class _SortButtonState extends ConsumerState<_SortButton> { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: RemoteAlbumSortMode.values @@ -188,33 +162,27 @@ class _SortButtonState extends ConsumerState<_SortButton> { (sortMode) => MenuItemButton( leadingIcon: albumSortOption == sortMode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () => onMenuTapped(sortMode), style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( albumSortOption == sortMode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( @@ -243,12 +211,8 @@ class _SortButtonState extends ConsumerState<_SortButton> { Padding( padding: const EdgeInsets.only(right: 5), child: albumSortIsReverse - ? const Icon( - Icons.keyboard_arrow_down, - ) - : const Icon( - Icons.keyboard_arrow_up_rounded, - ), + ? const Icon(Icons.keyboard_arrow_down) + : const Icon(Icons.keyboard_arrow_up_rounded), ), Text( albumSortOption.key.t(context: context), @@ -287,13 +251,8 @@ class _SearchBar extends StatelessWidget { sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -311,10 +270,7 @@ class _SearchBar extends StatelessWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClearSearch) : null, controller: searchController, onChanged: (_) => onSearch(searchController.text, filterMode), @@ -362,10 +318,7 @@ class _QuickFilterButtonRow extends StatelessWidget { isSelected: filterMode == QuickFilterMode.sharedWithMe, onTap: () { onChangeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), _QuickFilterButton( @@ -373,10 +326,7 @@ class _QuickFilterButtonRow extends StatelessWidget { isSelected: filterMode == QuickFilterMode.myAlbums, onTap: () { onChangeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -387,11 +337,7 @@ class _QuickFilterButtonRow extends StatelessWidget { } class _QuickFilterButton extends StatelessWidget { - const _QuickFilterButton({ - required this.isSelected, - required this.onTap, - required this.label, - }); + const _QuickFilterButton({required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -402,18 +348,11 @@ class _QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), @@ -429,10 +368,7 @@ class _QuickFilterButton extends StatelessWidget { } class _QuickSortAndViewMode extends StatelessWidget { - const _QuickSortAndViewMode({ - required this.isGrid, - required this.onToggleViewMode, - }); + const _QuickSortAndViewMode({required this.isGrid, required this.onToggleViewMode}); final bool isGrid; final VoidCallback onToggleViewMode; @@ -447,10 +383,7 @@ class _QuickSortAndViewMode extends StatelessWidget { children: [ const _SortButton(), IconButton( - icon: Icon( - isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: onToggleViewMode, ), ], @@ -461,11 +394,7 @@ class _QuickSortAndViewMode extends StatelessWidget { } class _AlbumList extends ConsumerWidget { - const _AlbumList({ - required this.albums, - required this.userId, - required this.onAlbumSelected, - }); + const _AlbumList({required this.albums, required this.userId, required this.onAlbumSelected}); final List albums; final String? userId; @@ -476,10 +405,7 @@ class _AlbumList extends ConsumerWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -491,51 +417,25 @@ class _AlbumList extends ConsumerWidget { final album = albums[index]; return Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - ), + padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( title: Text( album.name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => onAlbumSelected(album), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: album.thumbnailAssetId != null ? ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: SizedBox( - width: 80, - height: 80, - child: Thumbnail( - remoteId: album.thumbnailAssetId, - ), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), ) : SizedBox( width: 80, @@ -544,16 +444,9 @@ class _AlbumList extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: const Icon( - Icons.photo_album_rounded, - size: 24, - color: Colors.grey, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), ), ), ), @@ -566,11 +459,7 @@ class _AlbumList extends ConsumerWidget { } class _AlbumGrid extends StatelessWidget { - const _AlbumGrid({ - required this.albums, - required this.userId, - required this.onAlbumSelected, - }); + const _AlbumGrid({required this.albums, required this.userId, required this.onAlbumSelected}); final List albums; final String? userId; @@ -581,10 +470,7 @@ class _AlbumGrid extends StatelessWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -598,28 +484,17 @@ class _AlbumGrid extends StatelessWidget { crossAxisSpacing: 4, childAspectRatio: .7, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final album = albums[index]; - return _GridAlbumCard( - album: album, - userId: userId, - onAlbumSelected: onAlbumSelected, - ); - }, - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final album = albums[index]; + return _GridAlbumCard(album: album, userId: userId, onAlbumSelected: onAlbumSelected); + }, childCount: albums.length), ), ); } } class _GridAlbumCard extends ConsumerWidget { - const _GridAlbumCard({ - required this.album, - required this.userId, - required this.onAlbumSelected, - }); + const _GridAlbumCard({required this.album, required this.userId, required this.onAlbumSelected}); final RemoteAlbum album; final String? userId; @@ -633,13 +508,8 @@ class _GridAlbumCard extends ConsumerWidget { elevation: 0, color: context.colorScheme.surfaceBright, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -647,22 +517,14 @@ class _GridAlbumCard extends ConsumerWidget { Expanded( flex: 2, child: ClipRRect( - borderRadius: const BorderRadius.vertical( - top: Radius.circular(15), - ), + borderRadius: const BorderRadius.vertical(top: Radius.circular(15)), child: SizedBox( width: double.infinity, child: album.thumbnailAssetId != null - ? Thumbnail( - remoteId: album.thumbnailAssetId, - ) + ? Thumbnail(remoteId: album.thumbnailAssetId) : Container( color: context.colorScheme.surfaceContainerHighest, - child: const Icon( - Icons.photo_album_rounded, - size: 40, - color: Colors.grey, - ), + child: const Icon(Icons.photo_album_rounded, size: 40, color: Colors.grey), ), ), ), @@ -679,27 +541,13 @@ class _GridAlbumCard extends ConsumerWidget { album.name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', maxLines: 1, overflow: TextOverflow.ellipsis, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], ), @@ -718,17 +566,15 @@ class AddToAlbumHeader extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { Future onCreateAlbum() async { - final newAlbum = await ref.read(remoteAlbumProvider.notifier).createAlbum( + final newAlbum = await ref + .read(remoteAlbumProvider.notifier) + .createAlbum( title: "Untitled Album", assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(), ); if (newAlbum == null) { - ImmichToast.show( - context: context, - toastType: ToastType.error, - msg: 'errors.failed_to_create_album'.tr(), - ); + ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr()); return; } @@ -736,38 +582,23 @@ class AddToAlbumHeader extends ConsumerWidget { } return SliverPadding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), sliver: SliverToBoxAdapter( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), // remove internal padding + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), // remove internal padding minimumSize: const Size(0, 0), // allow shrinking tapTargetSize: MaterialTapTargetSize.shrinkWrap, // remove extra height ), onPressed: onCreateAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index e78c8ea8ad..1eb3366e30 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -13,7 +13,5 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?>( - StackChildrenNotifier.new, -); +final stackChildrenNotifier = AsyncNotifierProvider.autoDispose + .family, BaseAsset?>(StackChildrenNotifier.new); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart index 92f516157e..e5d1487d53 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart @@ -11,9 +11,7 @@ class AssetStackRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { @@ -27,11 +25,10 @@ class AssetStackRow extends ConsumerWidget { child: AnimatedOpacity( opacity: opacity / 255, duration: Durations.short2, - child: ref.watch(stackChildrenNotifier(asset)).when( - data: (state) => SizedBox.square( - dimension: 80, - child: _StackList(stack: state), - ), + child: ref + .watch(stackChildrenNotifier(asset)) + .when( + data: (state) => SizedBox.square(dimension: 80, child: _StackList(stack: state)), error: (_, __) => const SizedBox.shrink(), loading: () => const SizedBox.shrink(), ), @@ -49,11 +46,7 @@ class _StackList extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return ListView.builder( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemCount: stack.length, itemBuilder: (ctx, index) { final asset = stack[index]; @@ -71,9 +64,7 @@ class _StackList extends ConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, @@ -87,10 +78,7 @@ class _StackList extends ConsumerWidget { children: [ Image( fit: BoxFit.cover, - image: getThumbnailImageProvider( - remoteId: asset.id, - size: const Size.square(60), - ), + image: getThumbnailImageProvider(remoteId: asset.id, size: const Size.square(60)), ), if (asset.isVideo) const Icon( @@ -98,11 +86,7 @@ class _StackList extends ConsumerWidget { color: Colors.white, size: 16, shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), + Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0)), ], ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 8fbc28f072..c6b2360c9d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -36,12 +36,7 @@ class AssetViewerPage extends StatelessWidget { final TimelineService timelineService; final int? heroOffset; - const AssetViewerPage({ - super.key, - required this.initialIndex, - required this.timelineService, - this.heroOffset, - }); + const AssetViewerPage({super.key, required this.initialIndex, required this.timelineService, this.heroOffset}); @override Widget build(BuildContext context) { @@ -59,12 +54,7 @@ class AssetViewer extends ConsumerStatefulWidget { final Platform? platform; final int? heroOffset; - const AssetViewer({ - super.key, - required this.initialIndex, - this.platform, - this.heroOffset, - }); + const AssetViewer({super.key, required this.initialIndex, this.platform, this.heroOffset}); @override ConsumerState createState() => _AssetViewerState(); @@ -162,11 +152,7 @@ class _AssetViewerState extends ConsumerState { context, onError: (_, __) {}, ), - precacheImage( - getFullImageProvider(asset, size: screenSize), - context, - onError: (_, __) {}, - ), + precacheImage(getFullImageProvider(asset, size: screenSize), context, onError: (_, __) {}), ]), ); } @@ -222,9 +208,7 @@ class _AssetViewerState extends ConsumerState { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -262,7 +246,8 @@ class _AssetViewerState extends ConsumerState { viewController = controller; dragDownPosition = details.localPosition; initialPhotoViewState = controller.value; - final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || + final isZoomed = + scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || scaleStateController.scaleState == PhotoViewScaleState.covering; if (!showingBottomSheet && isZoomed) { blockGestures = true; @@ -350,10 +335,7 @@ class _AssetViewerState extends ConsumerState { final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round(); - viewController?.updateMultiple( - position: initialPhotoViewState.position + delta, - scale: updatedScale, - ); + viewController?.updateMultiple(position: initialPhotoViewState.position + delta, scale: updatedScale); ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity); } @@ -450,32 +432,21 @@ class _AssetViewerState extends ConsumerState { }); } - void _openBottomSheet( - BuildContext ctx, { - double extent = _kBottomSheetMinimumExtent, - }) { + void _openBottomSheet(BuildContext ctx, {double extent = _kBottomSheetMinimumExtent}) { ref.read(assetViewerProvider.notifier).setBottomSheet(true); initialScale = viewController?.scale; viewController?.updateMultiple(scale: _getScaleForBottomSheet); previousExtent = _kBottomSheetMinimumExtent; sheetCloseController = showBottomSheet( context: ctx, - sheetAnimationStyle: const AnimationStyle( - duration: Durations.short4, - reverseDuration: Durations.short2, - ), + sheetAnimationStyle: const AnimationStyle(duration: Durations.short4, reverseDuration: Durations.short2), constraints: const BoxConstraints(maxWidth: double.infinity), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), backgroundColor: ctx.colorScheme.surfaceContainerLowest, builder: (_) { return NotificationListener( onNotification: _onNotification, - child: AssetDetailBottomSheet( - controller: bottomSheetController, - initialChildSize: extent, - ), + child: AssetDetailBottomSheet(controller: bottomSheetController, initialChildSize: extent), ); }, ); @@ -496,18 +467,10 @@ class _AssetViewerState extends ConsumerState { return; } isSnapping = true; - bottomSheetController.animateTo( - _kBottomSheetSnapExtent, - duration: Durations.short3, - curve: Curves.easeOut, - ); + bottomSheetController.animateTo(_kBottomSheetSnapExtent, duration: Durations.short3, curve: Curves.easeOut); } - Widget _placeholderBuilder( - BuildContext ctx, - ImageChunkEvent? progress, - int index, - ) { + Widget _placeholderBuilder(BuildContext ctx, ImageChunkEvent? progress, int index) { BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; if (stackChildren != null && stackChildren.isNotEmpty) { @@ -517,14 +480,7 @@ class _AssetViewerState extends ConsumerState { width: double.infinity, height: double.infinity, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: Size( - ctx.width, - ctx.height, - ), - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: Size(ctx.width, ctx.height)), ); } @@ -574,11 +530,7 @@ class _AssetViewerState extends ConsumerState { width: ctx.width, height: ctx.height, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: size, - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: size), ), ); } @@ -662,8 +614,7 @@ class _AssetViewerState extends ConsumerState { pageController: pageController, scrollPhysics: platform.isIOS ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - , + : const FastClampingScrollPhysics(), // Use heavy physics for Android itemCount: totalAssets, onPageChanged: _onPageChanged, onPageBuild: _onPageBuild, @@ -678,10 +629,7 @@ class _AssetViewerState extends ConsumerState { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const AssetStackRow(), - if (!isInLockedView) const ViewerBottomBar(), - ], + children: [const AssetStackRow(), if (!isInLockedView) const ViewerBottomBar()], ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 32d5249bdc..88513516eb 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -79,17 +79,11 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { } void setOpacity(int opacity) { - state = state.copyWith( - backgroundOpacity: opacity, - showingControls: opacity == 255 ? true : state.showingControls, - ); + state = state.copyWith(backgroundOpacity: opacity, showingControls: opacity == 255 ? true : state.showingControls); } void setBottomSheet(bool showing) { - state = state.copyWith( - showingBottomSheet: showing, - showingControls: showing ? true : state.showingControls, - ); + state = state.copyWith(showingBottomSheet: showing, showingControls: showing ? true : state.showingControls); if (showing) { ref.read(videoPlayerControlsProvider.notifier).pause(); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 8c04fd5a85..881ed4d150 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -25,12 +25,8 @@ class ViewerBottomBar extends ConsumerWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; - final isSheetOpen = ref.watch( - assetViewerProvider.select((s) => s.showingBottomSheet), - ); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { @@ -42,13 +38,8 @@ class ViewerBottomBar extends ConsumerWidget { if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), asset.isLocalOnly - ? const DeleteLocalActionButton( - source: ActionSource.viewer, - ) - : const DeleteActionButton( - source: ActionSource.viewer, - showConfirmation: true, - ), + ? const DeleteLocalActionButton(source: ActionSource.viewer) + : const DeleteActionButton(source: ActionSource.viewer, showConfirmation: true), ]; return IgnorePointer( @@ -64,9 +55,7 @@ class ViewerBottomBar extends ConsumerWidget { data: context.themeData.copyWith( iconTheme: const IconThemeData(size: 22, color: Colors.white), textTheme: context.themeData.textTheme.copyWith( - labelLarge: context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), + labelLarge: context.themeData.textTheme.labelLarge?.copyWith(color: Colors.white), ), ), child: Container( @@ -77,10 +66,7 @@ class ViewerBottomBar extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), ], ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index e24bb3d7c0..73ec6b456a 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -29,11 +29,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { final DraggableScrollableController? controller; final double initialChildSize; - const AssetDetailBottomSheet({ - this.controller, - this.initialChildSize = 0.35, - super.key, - }); + const AssetDetailBottomSheet({this.controller, this.initialChildSize = 0.35, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -42,9 +38,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { return const SizedBox.shrink(); } - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); final isInLockedView = ref.watch(inLockedViewProvider); @@ -58,9 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { ? const TrashActionButton(source: ActionSource.viewer) : const DeletePermanentActionButton(source: ActionSource.viewer), const DeleteActionButton(source: ActionSource.viewer), - const MoveToLockFolderActionButton( - source: ActionSource.viewer, - ), + const MoveToLockFolderActionButton(source: ActionSource.viewer), ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), @@ -153,9 +145,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), - titleStyle: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), const SheetLocationDetails(), // Details header @@ -185,11 +175,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { _SheetTile( title: cameraTitle, titleStyle: context.textTheme.labelLarge, - leading: Icon( - Icons.camera_outlined, - size: 24, - color: context.textTheme.labelLarge?.color, - ), + leading: Icon(Icons.camera_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getCameraInfoSubtitle(exifInfo), subtitleStyle: context.textTheme.bodyMedium?.copyWith( color: context.textTheme.bodyMedium?.color?.withAlpha(155), @@ -207,13 +193,7 @@ class _SheetTile extends StatelessWidget { final TextStyle? titleStyle; final TextStyle? subtitleStyle; - const _SheetTile({ - required this.title, - this.titleStyle, - this.leading, - this.subtitle, - this.subtitleStyle, - }); + const _SheetTile({required this.title, this.titleStyle, this.leading, this.subtitle, this.subtitleStyle}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart index f91dafb3ed..ab57ea4d8b 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart @@ -38,20 +38,13 @@ class _SheetLocationDetailsState extends ConsumerState { _mapController = controller; } - void _onExifChanged( - AsyncValue? previous, - AsyncValue current, - ) { + void _onExifChanged(AsyncValue? previous, AsyncValue current) { asset = ref.read(currentAssetNotifier); setState(() { exifInfo = current.valueOrNull; final hasCoordinates = exifInfo?.hasCoordinates ?? false; if (exifInfo != null && hasCoordinates) { - _mapController?.moveCamera( - CameraUpdate.newLatLng( - LatLng(exifInfo!.latitude!, exifInfo!.longitude!), - ), - ); + _mapController?.moveCamera(CameraUpdate.newLatLng(LatLng(exifInfo!.latitude!, exifInfo!.longitude!))); } }); } @@ -59,11 +52,7 @@ class _SheetLocationDetailsState extends ConsumerState { @override void initState() { super.initState(); - ref.listenManual( - currentAssetExifProvider, - _onExifChanged, - fireImmediately: true, - ); + ref.listenManual(currentAssetExifProvider, _onExifChanged, fireImmediately: true); } @override @@ -80,10 +69,7 @@ class _SheetLocationDetailsState extends ConsumerState { final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; return Padding( - padding: EdgeInsets.symmetric( - vertical: 16.0, - horizontal: context.isMobile ? 16.0 : 56.0, - ), + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: context.isMobile ? 16.0 : 56.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -97,25 +83,16 @@ class _SheetLocationDetailsState extends ConsumerState { ), ), ), - ExifMap( - exifInfo: exifInfo!, - markerId: remoteId, - onMapCreated: _onMapCreated, - ), + ExifMap(exifInfo: exifInfo!, markerId: remoteId, onMapCreated: _onMapCreated), const SizedBox(height: 15), if (locationName != null) Padding( padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - locationName, - style: context.textTheme.labelLarge, - ), + child: Text(locationName, style: context.textTheme.labelLarge), ), Text( coordinates, - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index f1163ad2fd..450012f7fa 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -36,25 +36,18 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final showViewInTimelineButton = previousRouteName != TabShellRoute.name && previousRouteName != null; final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; } - final isCasting = ref.watch( - castProvider.select((c) => c.isCasting), - ); + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected)); final actions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, - ), + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), if (showViewInTimelineButton) IconButton( onPressed: () async { @@ -68,19 +61,13 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { if (asset.hasRemote && isOwner && !asset.isFavorite) const FavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.hasRemote && isOwner && asset.isFavorite) - const UnFavoriteActionButton( - source: ActionSource.viewer, - menuItem: true, - ), + const UnFavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true), const _KebabMenu(), ]; final lockedViewActions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, - ), + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), const _KebabMenu(), ]; @@ -98,8 +85,8 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { actions: isShowingSheet ? null : isInLockedView - ? lockedViewActions - : actions, + ? lockedViewActions + : actions, ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index f0d665b8ce..32510c2ca5 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,10 +27,7 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; -bool _isCurrentAsset( - BaseAsset asset, - BaseAsset? currentAsset, -) { +bool _isCurrentAsset(BaseAsset asset, BaseAsset? currentAsset) { if (asset is RemoteAsset) { return switch (currentAsset) { RemoteAsset remoteAsset => remoteAsset.id == asset.id, @@ -98,10 +95,7 @@ class NativeVideoViewer extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } @@ -122,31 +116,24 @@ class NativeVideoViewer extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.name}: $error', - ); + log.severe('Error creating video source for asset ${asset.name}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(null); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.name}: $error', - ); - } - }, - [asset.heroTag], - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.name}: $error'); + } + }, [asset.heroTag]); void checkIfBuffering() { if (!context.mounted) { @@ -156,8 +143,9 @@ class NativeVideoViewer extends HookConsumerWidget { final videoPlayback = ref.read(videoPlaybackValueProvider); if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -345,48 +333,42 @@ class NativeVideoViewer extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -416,12 +398,7 @@ class NativeVideoViewer extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart index 1fc01bb8e5..c1324b8ac0 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart @@ -13,16 +13,11 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class VideoViewerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const VideoViewerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const VideoViewerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetNotifier.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetNotifier.select((asset) => asset != null && asset.isVideo)); bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); if (showBottomSheet) { @@ -33,20 +28,17 @@ class VideoViewerControls extends HookConsumerWidget { final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { - ref.read(assetViewerProvider.notifier).setControls(false); - } - }, - ); + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(assetViewerProvider.notifier).setControls(false); + } + }); final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them @@ -97,11 +89,7 @@ class VideoViewerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( onTap: () => ref.read(assetViewerProvider.notifier).setControls(false), diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart index 520111070f..a74c169224 100644 --- a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -11,11 +11,7 @@ class BackupToggleButton extends ConsumerStatefulWidget { final VoidCallback onStart; final VoidCallback onStop; - const BackupToggleButton({ - super.key, - required this.onStart, - required this.onStop, - }); + const BackupToggleButton({super.key, required this.onStart, required this.onStop}); @override ConsumerState createState() => BackupToggleButtonState(); @@ -29,17 +25,12 @@ class BackupToggleButtonState extends ConsumerState with Sin @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 8), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 8), vsync: this); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); } @@ -66,21 +57,13 @@ class BackupToggleButtonState extends ConsumerState with Sin @override Widget build(BuildContext context) { - final enqueueCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueCount), - ); + final enqueueCount = ref.watch(driftBackupProvider.select((state) => state.enqueueCount)); - final enqueueTotalCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueTotalCount), - ); + final enqueueTotalCount = ref.watch(driftBackupProvider.select((state) => state.enqueueTotalCount)); - final isCanceling = ref.watch( - driftBackupProvider.select((state) => state.isCanceling), - ); + final isCanceling = ref.watch(driftBackupProvider.select((state) => state.isCanceling)); - final uploadTasks = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); final isUploading = uploadTasks.isNotEmpty; @@ -116,11 +99,7 @@ class BackupToggleButtonState extends ConsumerState with Sin end: Alignment.bottomRight, ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 12, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 2)), ], ), child: Container( @@ -151,18 +130,8 @@ class BackupToggleButtonState extends ConsumerState with Sin ), ), child: isUploading - ? const SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) - : Icon( - Icons.cloud_upload_outlined, - color: context.primaryColor, - size: 24, - ), + ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) + : Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24), ), const SizedBox(width: 16), Expanded( @@ -185,10 +154,7 @@ class BackupToggleButtonState extends ConsumerState with Sin Text( "queue_status".t( context: context, - args: { - 'count': enqueueCount.toString(), - 'total': enqueueTotalCount.toString(), - }, + args: {'count': enqueueCount.toString(), 'total': enqueueTotalCount.toString()}, ), style: context.textTheme.labelLarge?.copyWith( color: context.colorScheme.onSurfaceSecondary, @@ -197,10 +163,7 @@ class BackupToggleButtonState extends ConsumerState with Sin if (isCanceling) Row( children: [ - Text( - "canceling".t(), - style: context.textTheme.labelLarge, - ), + Text("canceling".t(), style: context.textTheme.labelLarge), const SizedBox(width: 4), SizedBox( width: 18, @@ -215,10 +178,7 @@ class BackupToggleButtonState extends ConsumerState with Sin ], ), ), - Switch.adaptive( - value: _isEnabled, - onChanged: (value) => isCanceling ? null : _onToggle(value), - ), + Switch.adaptive(value: _isEnabled, onChanged: (value) => isCanceling ? null : _onToggle(value)), ], ), ), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 76243cf803..6485926996 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -24,9 +24,7 @@ class ArchiveBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,14 +39,10 @@ class ArchiveBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index b3e71567e0..a2c88d9fd7 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -73,9 +73,7 @@ class _BaseDraggableScrollableSheetState extends ConsumerState borderOnForeground: false, clipBehavior: Clip.antiAlias, elevation: 6.0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(18))), margin: const EdgeInsets.symmetric(horizontal: 0), child: CustomScrollView( controller: scrollController, @@ -89,11 +87,7 @@ class _BaseDraggableScrollableSheetState extends ConsumerState if (widget.actions.isNotEmpty) SizedBox( height: 115, - child: ListView( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: widget.actions, - ), + child: ListView(shrinkWrap: true, scrollDirection: Axis.horizontal, children: widget.actions), ), if (widget.actions.isNotEmpty) ...[ const Divider(indent: 16, endIndent: 16), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index 3f3f933745..ec0fded6c3 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -24,9 +24,7 @@ class FavoriteBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,14 +39,10 @@ class FavoriteBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index d338cfa833..3912aef15c 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -31,9 +31,7 @@ class GeneralBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); Future addAssetsToAlbum(RemoteAlbum album) async { final selectedAssets = multiselect.selectedAssets; @@ -41,24 +39,19 @@ class GeneralBottomSheet extends ConsumerWidget { return; } - final addedCount = await ref.read(remoteAlbumProvider.notifier).addAssets( - album.id, - selectedAssets.map((e) => (e as RemoteAsset).id).toList(), - ); + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); if (addedCount != selectedAssets.length) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } @@ -78,18 +71,14 @@ class GeneralBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const DeleteActionButton(source: ActionSource.timeline), if (multiselect.hasLocal || multiselect.hasMerged) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), ], const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ @@ -99,9 +88,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], slivers: [ const AddToAlbumHeader(), - AlbumSelector( - onAlbumSelected: addAssetsToAlbum, - ), + AlbumSelector(onAlbumSelected: addAssetsToAlbum), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index ff77c79906..9765b61684 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -27,9 +27,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -44,24 +42,17 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton( - source: ActionSource.timeline, - albumId: album.id, - ), + RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: album.id), ], ); } diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index e4effd0804..d94480b434 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -5,20 +5,12 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; -ImageProvider getFullImageProvider( - BaseAsset asset, { - Size size = const Size(1080, 1920), -}) { +ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) { // Create new provider and cache it final ImageProvider provider; if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - provider = LocalFullImageProvider( - id: id, - name: asset.name, - size: size, - type: asset.type, - ); + provider = LocalFullImageProvider(id: id, name: asset.name, size: size, type: asset.type); } else { final String assetId; if (asset is LocalAsset && asset.hasRemote) { @@ -34,15 +26,8 @@ ImageProvider getFullImageProvider( return provider; } -ImageProvider getThumbnailImageProvider({ - BaseAsset? asset, - String? remoteId, - Size size = const Size.square(256), -}) { - assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); +ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = const Size.square(256)}) { + assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); if (remoteId != null) { return RemoteThumbProvider(assetId: remoteId); @@ -50,12 +35,7 @@ ImageProvider getThumbnailImageProvider({ if (_shouldUseLocalAsset(asset!)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - return LocalThumbProvider( - id: id, - updatedAt: asset.updatedAt, - name: asset.name, - size: size, - ); + return LocalThumbProvider(id: id, updatedAt: asset.updatedAt, name: asset.name, size: size); } final String assetId; diff --git a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart index dcf0f28527..8b9ede4c6d 100644 --- a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; class LocalAlbumThumbnail extends ConsumerWidget { - const LocalAlbumThumbnail({ - super.key, - required this.albumId, - }); + const LocalAlbumThumbnail({super.key, required this.albumId}); final String albumId; @override @@ -21,34 +18,21 @@ class LocalAlbumThumbnail extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: Icon( - Icons.collections, - size: 24, - color: context.primaryColor, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: Icon(Icons.collections, size: 24, color: context.primaryColor), ); } return ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Thumbnail( - asset: data, - ), + child: Thumbnail(asset: data), ); }, error: (error, stack) { return const Icon(Icons.error, size: 24); }, - loading: () => const SizedBox( - width: 24, - height: 24, - child: Center(child: CircularProgressIndicator()), - ), + loading: () => const SizedBox(width: 24, height: 24, child: Center(child: CircularProgressIndicator())), ); } } diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 41bc19ba57..350bcbb8fb 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -39,10 +39,7 @@ class LocalThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiFrameImageStreamCompleter( codec: _codec(key, cache, decode), @@ -57,11 +54,7 @@ class LocalThumbProvider extends ImageProvider { ); } - Future _codec( - LocalThumbProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async { + Future _codec(LocalThumbProvider key, CacheManager cache, ImageDecoderCallback decode) async { final cacheKey = '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}'; final fileFromCache = await cache.getFileFromCache(cacheKey); @@ -75,9 +68,7 @@ class LocalThumbProvider extends ImageProvider { final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbnailBytes == null) { PaintingBinding.instance.imageCache.evict(key); - throw StateError( - "Loading thumb for local photo ${key.name} failed", - ); + throw StateError("Loading thumb for local photo ${key.name} failed"); } final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); @@ -107,12 +98,7 @@ class LocalFullImageProvider extends ImageProvider { final Size size; final AssetType type; - const LocalFullImageProvider({ - required this.id, - required this.name, - required this.size, - required this.type, - }); + const LocalFullImageProvider({required this.id, required this.name, required this.size, required this.type}); @override Future obtainKey(ImageConfiguration configuration) { @@ -120,10 +106,7 @@ class LocalFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) { return MultiImageStreamCompleter( codec: _codec(key, decode), scale: 1.0, @@ -134,10 +117,7 @@ class LocalFullImageProvider extends ImageProvider { } // Streams in each stage of the image as we ask for it - Stream _codec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { try { switch (key.type) { case AssetType.image: @@ -156,16 +136,11 @@ class LocalFullImageProvider extends ImageProvider { } } catch (error, stack) { Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack); - throw const ImageLoadingException( - 'Could not load image from local storage', - ); + throw const ImageLoadingException('Could not load image from local storage'); } } - Future _getThumbnailCodec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async { + Future _getThumbnailCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async { final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbBytes == null) { return null; @@ -174,10 +149,7 @@ class LocalFullImageProvider extends ImageProvider { return decode(buffer); } - Stream _decodeProgressive( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _decodeProgressive(LocalFullImageProvider key, ImageDecoderCallback decode) async* { final file = await _storageRepository.getFileForAsset(key.id); if (file == null) { throw StateError("Opening file for asset ${key.name} failed"); diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart index 14d13a08d8..27f310f4f2 100644 --- a/mobile/lib/presentation/widgets/images/remote_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -15,10 +15,7 @@ class RemoteThumbProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteThumbProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteThumbProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -26,10 +23,7 @@ class RemoteThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkController = StreamController(); return MultiFrameImageStreamCompleter( @@ -49,9 +43,7 @@ class RemoteThumbProvider extends ImageProvider { ImageDecoderCallback decode, StreamController chunkController, ) async { - final preview = getThumbnailUrlForRemoteId( - key.assetId, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId); return ImageLoader.loadImageFromCache( preview, @@ -79,10 +71,7 @@ class RemoteFullImageProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteFullImageProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteFullImageProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -90,10 +79,7 @@ class RemoteFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( diff --git a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart index cd286a4cdf..8d292523d7 100644 --- a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart +++ b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart @@ -8,9 +8,7 @@ import 'package:thumbhash/thumbhash.dart'; class ThumbHashProvider extends ImageProvider { final String thumbHash; - const ThumbHashProvider({ - required this.thumbHash, - }); + const ThumbHashProvider({required this.thumbHash}); @override Future obtainKey(ImageConfiguration configuration) { @@ -18,20 +16,11 @@ class ThumbHashProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadCodec(key, decode), - scale: 1.0, - ); + ImageStreamCompleter loadImage(ThumbHashProvider key, ImageDecoderCallback decode) { + return MultiFrameImageStreamCompleter(codec: _loadCodec(key, decode), scale: 1.0); } - Future _loadCodec( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) async { + Future _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) async { final image = thumbHashToRGBA(base64Decode(key.thumbHash)); return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image))); } diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 80f6af617c..8335bd406b 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -8,16 +8,8 @@ import 'package:logging/logging.dart'; import 'package:octo_image/octo_image.dart'; class Thumbnail extends StatelessWidget { - const Thumbnail({ - this.asset, - this.remoteId, - this.size = const Size.square(256), - this.fit = BoxFit.cover, - super.key, - }) : assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); + const Thumbnail({this.asset, this.remoteId, this.size = const Size.square(256), this.fit = BoxFit.cover, super.key}) + : assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); final BaseAsset? asset; final String? remoteId; @@ -33,12 +25,7 @@ class Thumbnail extends StatelessWidget { image: provider, octoSet: OctoSet( placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit), - errorBuilder: _blurHashErrorBuilder( - thumbHash, - provider: provider, - fit: fit, - asset: asset, - ), + errorBuilder: _blurHashErrorBuilder(thumbHash, provider: provider, fit: fit, asset: asset), ), fadeOutDuration: const Duration(milliseconds: 100), fadeInDuration: Duration.zero, @@ -50,10 +37,7 @@ class Thumbnail extends StatelessWidget { } } -OctoPlaceholderBuilder _blurHashPlaceholderBuilder( - String? thumbHash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder _blurHashPlaceholderBuilder(String? thumbHash, {BoxFit? fit}) { return (context) => thumbHash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( @@ -63,12 +47,7 @@ OctoPlaceholderBuilder _blurHashPlaceholderBuilder( ); } -OctoErrorBuilder _blurHashErrorBuilder( - String? blurhash, { - BaseAsset? asset, - ImageProvider? provider, - BoxFit? fit, -}) => +OctoErrorBuilder _blurHashErrorBuilder(String? blurhash, {BaseAsset? asset, ImageProvider? provider, BoxFit? fit}) => (context, e, s) { Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s); provider?.evict(); @@ -76,10 +55,7 @@ OctoErrorBuilder _blurHashErrorBuilder( alignment: Alignment.center, children: [ _blurHashPlaceholderBuilder(blurhash, fit: fit)(context), - const Opacity( - opacity: 0.75, - child: Icon(Icons.error_outline_rounded), - ), + const Opacity(opacity: 0.75, child: Icon(Icons.error_outline_rounded)), ], ); }; diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce4e50cbd5..b9ef1ca45a 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -30,29 +30,25 @@ class ThumbnailTile extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final heroIndex = heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.4) + : context.primaryColor.lighten(amount: 0.75); final isSelected = ref.watch( - multiSelectProvider.select( - (multiselect) => multiselect.selectedAssets.contains(asset), - ), + multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); final borderStyle = lockSelection ? BoxDecoration( color: context.colorScheme.surfaceContainerHighest, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 6, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6), ) : isSelected - ? BoxDecoration( - color: assetContainerColor, - border: Border.all(color: assetContainerColor, width: 6), - ) - : const BoxDecoration(); + ? BoxDecoration( + color: assetContainerColor, + border: Border.all(color: assetContainerColor, width: 6), + ) + : const BoxDecoration(); final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; @@ -63,28 +59,22 @@ class ThumbnailTile extends ConsumerWidget { curve: Curves.decelerate, decoration: borderStyle, child: ClipRRect( - borderRadius: - isSelected || lockSelection ? const BorderRadius.all(Radius.circular(15.0)) : BorderRadius.zero, + borderRadius: isSelected || lockSelection + ? const BorderRadius.all(Radius.circular(15.0)) + : BorderRadius.zero, child: Stack( children: [ Positioned.fill( child: Hero( tag: '${asset.heroTag}_$heroIndex', - child: Thumbnail( - asset: asset, - fit: fit, - size: size, - ), + child: Thumbnail(asset: asset, fit: fit, size: size), ), ), if (hasStack) Align( alignment: Alignment.topRight, child: Padding( - padding: EdgeInsets.only( - right: 10.0, - top: asset.isVideo ? 24.0 : 6.0, - ), + padding: EdgeInsets.only(right: 10.0, top: asset.isVideo ? 24.0 : 6.0), child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), @@ -99,26 +89,26 @@ class ThumbnailTile extends ConsumerWidget { if (showStorageIndicator) switch (asset.storage) { AssetState.local => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_off_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_off_outlined), ), + ), AssetState.remote => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_outlined), ), + ), AssetState.merged => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_done_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_done_outlined), ), + ), }, if (asset.isFavorite) const Align( @@ -154,41 +144,22 @@ class _SelectionIndicator extends StatelessWidget { final bool isLocked; final Color? color; - const _SelectionIndicator({ - required this.isSelected, - required this.isLocked, - this.color, - }); + const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); @override Widget build(BuildContext context) { if (isLocked) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: const Icon( - Icons.check_circle_rounded, - color: Colors.grey, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: const Icon(Icons.check_circle_rounded, color: Colors.grey), ); } else if (isSelected) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } else { - return const Icon( - Icons.circle_outlined, - color: Colors.white, - ); + return const Icon(Icons.circle_outlined, color: Colors.white); } } } @@ -212,12 +183,7 @@ class _VideoIndicator extends StatelessWidget { color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6))], ), ), const _TileOverlayIcon(Icons.play_circle_outline_rounded), @@ -237,13 +203,7 @@ class _TileOverlayIcon extends StatelessWidget { icon, color: Colors.white, size: 16, - shadows: [ - const Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], + shadows: [const Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart index 943b74288f..f067bc6bf3 100644 --- a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/routing/router.dart'; class DriftMemoryBottomInfo extends StatelessWidget { final DriftMemory memory; final String title; - const DriftMemoryBottomInfo({ - super.key, - required this.memory, - required this.title, - }); + const DriftMemoryBottomInfo({super.key, required this.memory, required this.title}); @override Widget build(BuildContext context) { @@ -23,47 +19,39 @@ class DriftMemoryBottomInfo extends StatelessWidget { final fileCreatedDate = memory.assets.first.createdAt; return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format(fileCreatedDate), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, + Text( + df.format(fileCreatedDate), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - ), - ], - ), - Tooltip( - message: 'view_in_timeline'.tr(), - child: MaterialButton( - minWidth: 0, - onPressed: () async { - await context.maybePop(); - await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); - EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], + ), + Tooltip( + message: 'view_in_timeline'.tr(), + child: MaterialButton( + minWidth: 0, + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), ), ), - ), - ]), + ], + ), ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index e69c848f45..eaed60b204 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -29,17 +29,12 @@ class DriftMemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio @@ -55,11 +50,7 @@ class DriftMemoryCard extends StatelessWidget { } if (asset.isImage) { - return FullImage( - asset, - fit: fit, - size: const Size(double.infinity, double.infinity), - ); + return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity)); } else { return SizedBox( width: context.width, @@ -69,11 +60,7 @@ class DriftMemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: FullImage( - asset, - size: Size(context.width, context.height), - fit: BoxFit.contain, - ), + image: FullImage(asset, size: Size(context.width, context.height), fit: BoxFit.contain), ), ); } @@ -85,10 +72,7 @@ class DriftMemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -109,16 +93,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -129,16 +106,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: getFullImageProvider( - asset, - size: Size(context.width, context.height), - ), + image: getFullImageProvider(asset, size: Size(context.width, context.height)), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index 4863b60aad..e2bc59b4c7 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -17,17 +17,13 @@ class DriftMemoryLane extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (index) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -40,12 +36,7 @@ class DriftMemoryLane extends ConsumerWidget { } } - context.pushRoute( - DriftMemoryRoute( - memories: memories, - memoryIndex: index, - ), - ); + context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)); }, children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), ), @@ -54,53 +45,33 @@ class DriftMemoryLane extends ConsumerWidget { } class DriftMemoryCard extends ConsumerWidget { - const DriftMemoryCard({ - super.key, - required this.memory, - }); + const DriftMemoryCard({super.key, required this.memory}); final DriftMemory memory; @override Widget build(BuildContext context, WidgetRef ref) { final yearsAgo = DateTime.now().year - memory.data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); return Center( child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: SizedBox( width: 205, height: 200, - child: Thumbnail( - remoteId: memory.assets[0].id, - fit: BoxFit.cover, - ), + child: Thumbnail(remoteId: memory.assets[0].id, fit: BoxFit.cover), ), ), Positioned( bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart index 81989b263a..c08348e2af 100644 --- a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart +++ b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart @@ -25,9 +25,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: FontWeight.w600, - ); + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); return SafeArea( child: Padding( @@ -38,72 +36,46 @@ class DriftRemoteAlbumOption extends ConsumerWidget { if (onEditAlbum != null) ListTile( leading: const Icon(Icons.edit), - title: Text( - 'edit_album'.t(context: context), - style: textStyle, - ), + title: Text('edit_album'.t(context: context), style: textStyle), onTap: onEditAlbum, ), if (onAddPhotos != null) ListTile( leading: const Icon(Icons.add_a_photo), - title: Text( - 'add_photos'.t(context: context), - style: textStyle, - ), + title: Text('add_photos'.t(context: context), style: textStyle), onTap: onAddPhotos, ), if (onAddUsers != null) ListTile( leading: const Icon(Icons.group_add), - title: Text( - 'album_viewer_page_share_add_users'.t(context: context), - style: textStyle, - ), + title: Text('album_viewer_page_share_add_users'.t(context: context), style: textStyle), onTap: onAddUsers, ), if (onLeaveAlbum != null) ListTile( leading: const Icon(Icons.person_remove_rounded), - title: Text( - 'leave_album'.t(context: context), - style: textStyle, - ), + title: Text('leave_album'.t(context: context), style: textStyle), onTap: onLeaveAlbum, ), if (onToggleAlbumOrder != null) ListTile( leading: const Icon(Icons.swap_vert_rounded), - title: Text( - 'change_display_order'.t(context: context), - style: textStyle, - ), + title: Text('change_display_order'.t(context: context), style: textStyle), onTap: onToggleAlbumOrder, ), if (onCreateSharedLink != null) ListTile( leading: const Icon(Icons.link), - title: Text( - 'create_shared_link'.t(context: context), - style: textStyle, - ), + title: Text('create_shared_link'.t(context: context), style: textStyle), onTap: onCreateSharedLink, ), if (onDeleteAlbum != null) ...[ - const Divider( - indent: 16, - endIndent: 16, - ), + const Divider(indent: 16, endIndent: 16), ListTile( - leading: Icon( - Icons.delete, - color: context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + leading: Icon(Icons.delete, color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), title: Text( 'delete_album'.t(context: context), - style: textStyle.copyWith( - color: context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + style: textStyle.copyWith(color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), ), onTap: onDeleteAlbum, ), diff --git a/mobile/lib/presentation/widgets/timeline/fixed/row.dart b/mobile/lib/presentation/widgets/timeline/fixed/row.dart index 24f3c97125..3fe3cea3c9 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/row.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/row.dart @@ -16,11 +16,7 @@ class FixedTimelineRow extends MultiChildRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { - return RenderFixedRow( - dimension: dimension, - spacing: spacing, - textDirection: textDirection, - ); + return RenderFixedRow(dimension: dimension, spacing: spacing, textDirection: textDirection); } @override @@ -50,9 +46,9 @@ class RenderFixedRow extends RenderBox required double dimension, required double spacing, required TextDirection textDirection, - }) : _dimension = dimension, - _spacing = spacing, - _textDirection = textDirection { + }) : _dimension = dimension, + _spacing = spacing, + _textDirection = textDirection { addAll(children); } diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 88d113dcde..ea5f55b35e 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -33,8 +33,8 @@ class FixedSegment extends Segment { required super.headerExtent, required super.spacing, required super.header, - }) : assert(tileHeight != 0), - mainAxisExtend = tileHeight + spacing; + }) : assert(tileHeight != 0), + mainAxisExtend = tileHeight + spacing; @override double indexToLayoutOffset(int index) { @@ -64,12 +64,7 @@ class FixedSegment extends Segment { final numberOfAssets = math.min(columnCount, assetCount - assetIndex); if (index == firstIndex) { - return TimelineHeader( - bucket: bucket, - header: header, - height: headerExtent, - assetOffset: firstAssetIndex, - ); + return TimelineHeader(bucket: bucket, header: header, height: headerExtent, assetOffset: firstAssetIndex); } return _FixedSegmentRow( @@ -104,10 +99,7 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow( - context, - timelineService.getAssets(assetIndex, assetCount), - ); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount)); } return FutureBuilder>( @@ -122,12 +114,7 @@ class _FixedSegmentRow extends ConsumerWidget { } Widget _buildPlaceholder(BuildContext context) { - return SegmentBuilder.buildPlaceholder( - context, - assetCount, - size: Size.square(tileHeight), - spacing: spacing, - ); + return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } Widget _buildAssetRow(BuildContext context, List assets) { @@ -137,11 +124,7 @@ class _FixedSegmentRow extends ConsumerWidget { textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(assets[i].heroTag), - asset: assets[i], - assetIndex: assetIndex + i, - ), + _AssetTileWidget(key: ValueKey(assets[i].heroTag), asset: assets[i], assetIndex: assetIndex + i), ], ); } @@ -151,19 +134,9 @@ class _AssetTileWidget extends ConsumerWidget { final BaseAsset asset; final int assetIndex; - const _AssetTileWidget({ - super.key, - required this.asset, - required this.assetIndex, - }); + const _AssetTileWidget({super.key, required this.asset, required this.assetIndex}); - Future _handleOnTap( - BuildContext ctx, - WidgetRef ref, - int assetIndex, - BaseAsset asset, - int? heroOffset, - ) async { + Future _handleOnTap(BuildContext ctx, WidgetRef ref, int assetIndex, BaseAsset asset, int? heroOffset) async { final multiSelectState = ref.read(multiSelectProvider); if (multiSelectState.forceEnable || multiSelectState.isEnabled) { @@ -192,11 +165,7 @@ class _AssetTileWidget extends ConsumerWidget { } bool _getLockSelectionStatus(WidgetRef ref) { - final lockSelectionAssets = ref.read( - multiSelectProvider.select( - (state) => state.lockedSelectionAssets, - ), - ); + final lockSelectionAssets = ref.read(multiSelectProvider.select((state) => state.lockedSelectionAssets)); if (lockSelectionAssets.isEmpty) { return false; @@ -210,9 +179,7 @@ class _AssetTileWidget extends ConsumerWidget { final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; final lockSelection = _getLockSelectionStatus(ref); - final showStorageIndicator = ref.watch( - timelineArgsProvider.select((args) => args.showStorageIndicator), - ); + final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator)); return RepaintBoundary( child: GestureDetector( diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart index 5e260a4e68..b65582f976 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart @@ -35,8 +35,7 @@ class FixedSegmentBuilder extends SegmentBuilder { final timelineHeader = switch (groupBy) { GroupAssetsBy.month => HeaderType.month, - GroupAssetsBy.day || - GroupAssetsBy.auto => + GroupAssetsBy.day || GroupAssetsBy.auto => bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day, GroupAssetsBy.none => HeaderType.none, }; diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index da48acd7db..b8c6668a38 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -47,11 +47,7 @@ class TimelineHeader extends StatelessWidget { final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay; return Padding( - padding: EdgeInsets.only( - top: isMonthHeader ? 8.0 : 0.0, - left: 12.0, - right: 12.0, - ), + padding: EdgeInsets.only(top: isMonthHeader ? 8.0 : 0.0, left: 12.0, right: 12.0), child: SizedBox( height: height, child: Column( @@ -61,32 +57,17 @@ class TimelineHeader extends StatelessWidget { if (isMonthHeader) Row( children: [ - Text( - _formatMonth(context, date), - style: context.textTheme.labelLarge?.copyWith(fontSize: 24), - ), + Text(_formatMonth(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 24)), const Spacer(), - if (header != HeaderType.monthAndDay) - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + if (header != HeaderType.monthAndDay) _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), if (isDayHeader) Row( children: [ - Text( - _formatDay(context, date), - style: context.textTheme.labelLarge?.copyWith( - fontSize: 15, - ), - ), + Text(_formatDay(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 15)), const Spacer(), - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), ], @@ -100,10 +81,7 @@ class _BulkSelectIconButton extends ConsumerWidget { final Bucket bucket; final int assetOffset; - const _BulkSelectIconButton({ - required this.bucket, - required this.assetOffset, - }); + const _BulkSelectIconButton({required this.bucket, required this.assetOffset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -118,23 +96,12 @@ class _BulkSelectIconButton extends ConsumerWidget { return IconButton( onPressed: () { - ref.read(multiSelectProvider.notifier).toggleBucketSelection( - assetOffset, - bucket.assetCount, - ); + ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); ref.read(hapticFeedbackProvider.notifier).heavyImpact(); }, icon: isAllSelected - ? Icon( - Icons.check_circle_rounded, - size: 26, - color: context.primaryColor, - ) - : Icon( - Icons.check_circle_outline_rounded, - size: 26, - color: context.colorScheme.onSurfaceSecondary, - ), + ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) + : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), ); } } diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 6d23f169b7..be1d0f0873 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -43,10 +43,7 @@ class Scrubber extends ConsumerStatefulWidget { ConsumerState createState() => ScrubberState(); } -List<_Segment> _buildSegments({ - required List layoutSegments, - required double timelineHeight, -}) { +List<_Segment> _buildSegments({required List layoutSegments, required double timelineHeight}) { const double offsetThreshold = 20.0; final segments = <_Segment>[]; @@ -66,14 +63,7 @@ List<_Segment> _buildSegments({ final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year); - segments.add( - _Segment( - date: date, - startOffset: startOffset, - scrollLabel: label, - showSegment: showSegment, - ), - ); + segments.add(_Segment(date: date, startOffset: startOffset, scrollLabel: label, showSegment: showSegment)); lastDate = date; if (showSegment) { lastOffset = startOffset; @@ -109,27 +99,12 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi void initState() { super.initState(); _isDragging = false; - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); - _thumbAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastEaseInToSlowEaseOut, - ); - _labelAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); + _thumbAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastEaseInToSlowEaseOut); + _labelAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -143,10 +118,7 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi super.didUpdateWidget(oldWidget); if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) { - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); } } @@ -276,12 +248,10 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi } int _findLayoutSegmentIndex(_Segment segment) { - return widget.layoutSegments.indexWhere( - (layoutSegment) { - final bucket = layoutSegment.bucket as TimeBucket; - return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; - }, - ); + return widget.layoutSegments.indexWhere((layoutSegment) { + final bucket = layoutSegment.bucket as TimeBucket; + return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; + }); } void _scrollToLayoutSegment(int layoutSegmentIndex) { @@ -311,19 +281,13 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi if (_scrollController.hasClients == true) { // Cache to avoid multiple calls to [_currentOffset] final scrollOffset = _currentOffset; - final labelText = _segments - .lastWhereOrNull( - (segment) => segment.startOffset <= scrollOffset, - ) - ?.scrollLabel ?? + final labelText = + _segments.lastWhereOrNull((segment) => segment.startOffset <= scrollOffset)?.scrollLabel ?? _segments.firstOrNull?.scrollLabel; label = labelText != null ? Text( labelText, - style: ctx.textTheme.bodyLarge?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: ctx.textTheme.bodyLarge?.copyWith(color: Colors.white, fontWeight: FontWeight.bold), ) : null; } @@ -351,11 +315,7 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi onVerticalDragStart: _onDragStart, onVerticalDragUpdate: _onDragUpdate, onVerticalDragEnd: _onDragEnd, - child: _Scrubber( - thumbAnimation: _thumbAnimation, - labelAnimation: _labelAnimation, - label: label, - ), + child: _Scrubber(thumbAnimation: _thumbAnimation, labelAnimation: _labelAnimation, label: label), ), ), ), @@ -370,12 +330,7 @@ class _SegmentsLayer extends StatelessWidget { final double topPadding; final bool isDragging; - const _SegmentsLayer({ - super.key, - required this.segments, - required this.topPadding, - required this.isDragging, - }); + const _SegmentsLayer({super.key, required this.segments, required this.topPadding, required this.isDragging}); @override Widget build(BuildContext context) { @@ -389,9 +344,7 @@ class _SegmentsLayer extends StatelessWidget { key: ValueKey('segment_${segment.date.millisecondsSinceEpoch}'), top: topPadding + segment.startOffset, end: 100, - child: RepaintBoundary( - child: _SegmentWidget(segment), - ), + child: RepaintBoundary(child: _SegmentWidget(segment)), ), ) .toList(), @@ -419,10 +372,7 @@ class _SegmentWidget extends StatelessWidget { alignment: Alignment.center, child: Text( _segment.date.year.toString(), - style: context.textTheme.labelMedium?.copyWith( - fontFamily: "OverpassMono", - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelMedium?.copyWith(fontFamily: "OverpassMono", fontWeight: FontWeight.w600), ), ), ), @@ -436,11 +386,7 @@ class _ScrollLabel extends StatelessWidget { final Color backgroundColor; final Animation animation; - const _ScrollLabel({ - required this.label, - required this.backgroundColor, - required this.animation, - }); + const _ScrollLabel({required this.label, required this.backgroundColor, required this.animation}); @override Widget build(BuildContext context) { @@ -471,16 +417,13 @@ class _Scrubber extends StatelessWidget { final Animation thumbAnimation; final Animation labelAnimation; - const _Scrubber({ - this.label, - required this.thumbAnimation, - required this.labelAnimation, - }); + const _Scrubber({this.label, required this.thumbAnimation, required this.labelAnimation}); @override Widget build(BuildContext context) { - final backgroundColor = - context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary; + final backgroundColor = context.isDarkTheme + ? context.colorScheme.primary.darken(amount: .5) + : context.colorScheme.primary; return _SlideFadeTransition( animation: thumbAnimation, @@ -488,12 +431,7 @@ class _Scrubber extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - if (label != null) - _ScrollLabel( - label: label!, - backgroundColor: backgroundColor, - animation: labelAnimation, - ), + if (label != null) _ScrollLabel(label: label!, backgroundColor: backgroundColor, animation: labelAnimation), _CircularThumb(backgroundColor), ], ), @@ -519,9 +457,7 @@ class _CircularThumb extends StatelessWidget { topRight: Radius.circular(4.0), bottomRight: Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0)), - ), + child: Container(constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0))), ), ); } @@ -543,14 +479,8 @@ class _ArrowPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -566,11 +496,9 @@ class _SlideFadeTransition extends StatelessWidget { final Animation _animation; final Widget _child; - const _SlideFadeTransition({ - required Animation animation, - required Widget child, - }) : _animation = animation, - _child = child; + const _SlideFadeTransition({required Animation animation, required Widget child}) + : _animation = animation, + _child = child; @override Widget build(BuildContext context) { @@ -578,14 +506,8 @@ class _SlideFadeTransition extends StatelessWidget { animation: _animation, builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(_animation), - child: FadeTransition( - opacity: _animation, - child: _child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(_animation), + child: FadeTransition(opacity: _animation, child: _child), ), ); } @@ -597,19 +519,9 @@ class _Segment { final String scrollLabel; final bool showSegment; - const _Segment({ - required this.date, - required this.startOffset, - required this.scrollLabel, - this.showSegment = false, - }); + const _Segment({required this.date, required this.startOffset, required this.scrollLabel, this.showSegment = false}); - _Segment copyWith({ - DateTime? date, - double? startOffset, - String? scrollLabel, - bool? showSegment, - }) { + _Segment copyWith({DateTime? date, double? startOffset, String? scrollLabel, bool? showSegment}) { return _Segment( date: date ?? this.date, startOffset: startOffset ?? this.startOffset, diff --git a/mobile/lib/presentation/widgets/timeline/segment.model.dart b/mobile/lib/presentation/widgets/timeline/segment.model.dart index 6a20db4c98..bc5f974874 100644 --- a/mobile/lib/presentation/widgets/timeline/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/segment.model.dart @@ -37,8 +37,8 @@ abstract class Segment { required this.headerExtent, required this.spacing, required this.header, - }) : gridIndex = firstIndex + 1, - gridOffset = startOffset + headerExtent + spacing; + }) : gridIndex = firstIndex + 1, + gridOffset = startOffset + headerExtent + spacing; bool containsIndex(int index) => firstIndex <= index && index <= lastIndex; diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart index a746eab243..c80595a446 100644 --- a/mobile/lib/presentation/widgets/timeline/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -9,34 +9,26 @@ abstract class SegmentBuilder { final double spacing; final GroupAssetsBy groupBy; - const SegmentBuilder({ - required this.buckets, - this.spacing = kTimelineSpacing, - this.groupBy = GroupAssetsBy.day, - }); + const SegmentBuilder({required this.buckets, this.spacing = kTimelineSpacing, this.groupBy = GroupAssetsBy.day}); static double headerExtent(HeaderType header) => switch (header) { - HeaderType.month => kTimelineHeaderExtent, - HeaderType.day => kTimelineHeaderExtent * 0.90, - HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, - HeaderType.none => 0.0, - }; + HeaderType.month => kTimelineHeaderExtent, + HeaderType.day => kTimelineHeaderExtent * 0.90, + HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, + HeaderType.none => 0.0, + }; static Widget buildPlaceholder( BuildContext context, int count, { Size size = const Size.square(kTimelineFixedTileExtent), double spacing = kTimelineSpacing, - }) => - RepaintBoundary( - child: FixedTimelineRow( - dimension: size.height, - spacing: spacing, - textDirection: Directionality.of(context), - children: List.generate( - count, - (_) => ThumbnailPlaceholder(width: size.width, height: size.height), - ), - ), - ); + }) => RepaintBoundary( + child: FixedTimelineRow( + dimension: size.height, + spacing: spacing, + textDirection: Directionality.of(context), + children: List.generate(count, (_) => ThumbnailPlaceholder(width: size.width, height: size.height)), + ), + ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index cdf79239d4..ad3ae3ccf6 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -54,10 +54,7 @@ class TimelineState { final bool isScrubbing; final bool isScrolling; - const TimelineState({ - this.isScrubbing = false, - this.isScrolling = false, - }); + const TimelineState({this.isScrubbing = false, this.isScrolling = false}); bool get isInteracting => isScrubbing || isScrolling; @@ -70,10 +67,7 @@ class TimelineState { int get hashCode => isScrubbing.hashCode ^ isScrolling.hashCode; TimelineState copyWith({bool? isScrubbing, bool? isScrolling}) { - return TimelineState( - isScrubbing: isScrubbing ?? this.isScrubbing, - isScrolling: isScrolling ?? this.isScrolling, - ); + return TimelineState(isScrubbing: isScrubbing ?? this.isScrubbing, isScrolling: isScrolling ?? this.isScrolling); } } @@ -89,38 +83,30 @@ class TimelineStateNotifier extends Notifier { } @override - TimelineState build() => const TimelineState( - isScrubbing: false, - isScrolling: false, - ); + TimelineState build() => const TimelineState(isScrubbing: false, isScrolling: false); } // This provider watches the buckets from the timeline service & args and serves the segments. // It should be used only after the timeline service and timeline args provider is overridden -final timelineSegmentProvider = StreamProvider.autoDispose>( - (ref) async* { - final args = ref.watch(timelineArgsProvider); - final columnCount = args.columnCount; - final spacing = args.spacing; - final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); - final tileExtent = math.max(0, availableTileWidth) / columnCount; +final timelineSegmentProvider = StreamProvider.autoDispose>((ref) async* { + final args = ref.watch(timelineArgsProvider); + final columnCount = args.columnCount; + final spacing = args.spacing; + final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); + final tileExtent = math.max(0, availableTileWidth) / columnCount; - final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; - final timelineService = ref.watch(timelineServiceProvider); - yield* timelineService.watchBuckets().map((buckets) { - return FixedSegmentBuilder( - buckets: buckets, - tileHeight: tileExtent, - columnCount: columnCount, - spacing: spacing, - groupBy: groupBy, - ).generate(); - }); - }, - dependencies: [timelineServiceProvider, timelineArgsProvider], -); + final timelineService = ref.watch(timelineServiceProvider); + yield* timelineService.watchBuckets().map((buckets) { + return FixedSegmentBuilder( + buckets: buckets, + tileHeight: tileExtent, + columnCount: columnCount, + spacing: spacing, + groupBy: groupBy, + ).generate(); + }); +}, dependencies: [timelineServiceProvider, timelineArgsProvider]); -final timelineStateProvider = NotifierProvider( - TimelineStateNotifier.new, -); +final timelineStateProvider = NotifierProvider(TimelineStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 59cc018b28..26799580a2 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -29,11 +29,7 @@ class Timeline extends StatelessWidget { this.topSliverWidgetHeight, this.showStorageIndicator = false, this.withStack = false, - this.appBar = const ImmichSliverAppBar( - floating: true, - pinned: false, - snap: false, - ), + this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), this.bottomSheet = const GeneralBottomSheet(), this.groupBy, }); @@ -57,9 +53,7 @@ class Timeline extends StatelessWidget { (ref) => TimelineArgs( maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight, - columnCount: ref.watch( - settingsProvider.select((s) => s.get(Setting.tilesPerRow)), - ), + columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), showStorageIndicator: showStorageIndicator, withStack: withStack, groupBy: groupBy, @@ -79,12 +73,7 @@ class Timeline extends StatelessWidget { } class _SliverTimeline extends ConsumerStatefulWidget { - const _SliverTimeline({ - this.topSliverWidget, - this.topSliverWidgetHeight, - this.appBar, - this.bottomSheet, - }); + const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight, this.appBar, this.bottomSheet}); final Widget? topSliverWidget; final double? topSliverWidgetHeight; @@ -108,11 +97,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { void _onEvent(Event event) { switch (event) { case ScrollToTopEvent(): - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 250), - curve: Curves.easeInOut, - ); + _scrollController.animateTo(0, duration: const Duration(milliseconds: 250), curve: Curves.easeInOut); case ScrollToDateEvent scrollToDateEvent: _scrollToDate(scrollToDateEvent.date); case TimelineReloadEvent(): @@ -143,7 +128,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }); // If exact date not found, try to find the closest month - final fallbackSegment = targetSegment ?? + final fallbackSegment = + targetSegment ?? segments.firstWhereOrNull((segment) { if (segment.bucket is TimeBucket) { final segmentDate = (segment.bucket as TimeBucket).date; @@ -168,9 +154,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); - final isSelectionMode = ref.watch( - multiSelectProvider.select((s) => s.forceEnable), - ); + final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); return asyncSegments.widgetWhen( onData: (segments) { @@ -211,42 +195,26 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { addRepaintBoundaries: false, ), ), - const SliverPadding( - padding: EdgeInsets.only( - bottom: scrubberBottomPadding, - ), - ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), ], ), ), if (!isSelectionMode) ...[ Consumer( builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); + final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); if (isMultiSelectEnabled) { return child!; } return const SizedBox.shrink(); }, - child: const Positioned( - top: 60, - left: 25, - child: _MultiSelectStatusButton(), - ), + child: const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), ), if (widget.bottomSheet != null) Consumer( builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); + final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); if (isMultiSelectEnabled) { return child!; @@ -267,22 +235,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget { final List _segments; - const _SliverSegmentedList({ - required List segments, - required super.delegate, - }) : _segments = segments; + const _SliverSegmentedList({required List segments, required super.delegate}) : _segments = segments; @override - _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => _RenderSliverTimelineBoxAdaptor( - childManager: context as SliverMultiBoxAdaptorElement, - segments: _segments, - ); + _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => + _RenderSliverTimelineBoxAdaptor(childManager: context as SliverMultiBoxAdaptorElement, segments: _segments); @override - void updateRenderObject( - BuildContext context, - _RenderSliverTimelineBoxAdaptor renderObject, - ) { + void updateRenderObject(BuildContext context, _RenderSliverTimelineBoxAdaptor renderObject) { renderObject.segments = _segments; } } @@ -299,10 +259,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { markNeedsLayout(); } - _RenderSliverTimelineBoxAdaptor({ - required super.childManager, - required List segments, - }) : _segments = segments; + _RenderSliverTimelineBoxAdaptor({required super.childManager, required List segments}) + : _segments = segments; int getMinChildIndexForScrollOffset(double offset) => _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0; @@ -335,16 +293,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset); // Find the index of the last child that should be visible or in the trailing cache area. - final int? lastRequiredChildIndex = - targetScrollOffset.isFinite ? getMaxChildIndexForScrollOffset(targetScrollOffset) : null; + final int? lastRequiredChildIndex = targetScrollOffset.isFinite + ? getMaxChildIndexForScrollOffset(targetScrollOffset) + : null; // Remove children that are no longer visible or within the cache area. if (firstChild == null) { collectGarbage(0, 0); } else { final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); - final int trailingChildrenToRemove = - lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); + final int trailingChildrenToRemove = lastRequiredChildIndex == null + ? 0 + : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); collectGarbage(leadingChildrenToRemove, trailingChildrenToRemove); } @@ -352,10 +312,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // try to add the first child needed for the current scroll offset. if (firstChild == null) { final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex); - final bool childAdded = addInitialChild( - index: firstRequiredChildIndex, - layoutOffset: firstChildLayoutOffset, - ); + final bool childAdded = addInitialChild(index: firstRequiredChildIndex, layoutOffset: firstChildLayoutOffset); if (!childAdded) { // There are either no children, or we are past the end of all our children. @@ -408,16 +365,15 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // until we reach the [lastRequiredChildIndex] or run out of children. double calculatedMaxScrollOffset = double.infinity; - for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; - lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; - ++currentIndex) { + for ( + int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; + lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; + ++currentIndex + ) { RenderBox? child = childAfter(mostRecentlyLaidOutChild!); if (child == null || indexOf(child) != currentIndex) { - child = insertAndLayoutChild( - childConstraints, - after: mostRecentlyLaidOutChild, - ); + child = insertAndLayoutChild(childConstraints, after: mostRecentlyLaidOutChild); if (child == null) { final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset(); @@ -443,30 +399,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { ); assert(debugAssertChildListIsNonEmptyAndContiguous()); assert(indexOf(firstChild!) == firstRequiredChildIndex); - assert( - lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex, - ); + assert(lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex); - calculatedMaxScrollOffset = math.min( - calculatedMaxScrollOffset, - estimateMaxScrollOffset(), - ); + calculatedMaxScrollOffset = math.min(calculatedMaxScrollOffset, estimateMaxScrollOffset()); - final double paintExtent = calculatePaintOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double paintExtent = calculatePaintOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double cacheExtent = calculateCacheOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double cacheExtent = calculateCacheOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent; - final int? targetLastIndexForPaint = - targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null; + final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite + ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) + : null; final maxPaintExtent = math.max(paintExtent, calculatedMaxScrollOffset); @@ -477,7 +421,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Indicates if there's content scrolled off-screen. // This is true if the last child needed for painting is actually laid out, // or if the first child is partially visible. - hasVisualOverflow: (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || + hasVisualOverflow: + (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || constraints.scrollOffset > 0.0, cacheExtent: cacheExtent, ); @@ -500,16 +445,10 @@ class _MultiSelectStatusButton extends ConsumerWidget { final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); return ElevatedButton.icon( onPressed: () => ref.read(multiSelectProvider.notifier).reset(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( selectCount.toString(), - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ); } diff --git a/mobile/lib/providers/activity.provider.dart b/mobile/lib/providers/activity.provider.dart index 38b8caaed9..a867a5a281 100644 --- a/mobile/lib/providers/activity.provider.dart +++ b/mobile/lib/providers/activity.provider.dart @@ -36,12 +36,9 @@ class AlbumActivity extends _$AlbumActivity { } Future addComment(String comment) async { - final activity = await ref.watch(activityServiceProvider).addActivity( - albumId, - ActivityType.comment, - assetId: assetId, - comment: comment, - ); + final activity = await ref + .watch(activityServiceProvider) + .addActivity(albumId, ActivityType.comment, assetId: assetId, comment: comment); if (activity.hasValue) { final activities = state.valueOrNull ?? []; diff --git a/mobile/lib/providers/activity.provider.g.dart b/mobile/lib/providers/activity.provider.g.dart index af574b991a..dc927795f8 100644 --- a/mobile/lib/providers/activity.provider.g.dart +++ b/mobile/lib/providers/activity.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$AlbumActivity late final String albumId; late final String? assetId; - FutureOr> build( - String albumId, [ - String? assetId, - ]); + FutureOr> build(String albumId, [String? assetId]); } /// Maintains the current list of all activities for @@ -58,24 +55,15 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider call( - String albumId, [ - String? assetId, - ]) { - return AlbumActivityProvider( - albumId, - assetId, - ); + AlbumActivityProvider call(String albumId, [String? assetId]) { + return AlbumActivityProvider(albumId, assetId); } @override AlbumActivityProvider getProviderOverride( covariant AlbumActivityProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -96,30 +84,28 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. -class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< - AlbumActivity, List> { +class AlbumActivityProvider + extends + AutoDisposeAsyncNotifierProviderImpl> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => AlbumActivity() - ..albumId = albumId - ..assetId = assetId, - from: albumActivityProvider, - name: r'albumActivityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumActivityHash, - dependencies: AlbumActivityFamily._dependencies, - allTransitiveDependencies: - AlbumActivityFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + AlbumActivityProvider(String albumId, [String? assetId]) + : this._internal( + () => AlbumActivity() + ..albumId = albumId + ..assetId = assetId, + from: albumActivityProvider, + name: r'albumActivityProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumActivityHash, + dependencies: AlbumActivityFamily._dependencies, + allTransitiveDependencies: + AlbumActivityFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); AlbumActivityProvider._internal( super._createNotifier, { @@ -136,13 +122,8 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< final String? assetId; @override - FutureOr> runNotifierBuild( - covariant AlbumActivity notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + FutureOr> runNotifierBuild(covariant AlbumActivity notifier) { + return notifier.build(albumId, assetId); } @override @@ -166,7 +147,7 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< @override AutoDisposeAsyncNotifierProviderElement> - createElement() { + createElement() { return _AlbumActivityProviderElement(this); } @@ -198,8 +179,9 @@ mixin AlbumActivityRef on AutoDisposeAsyncNotifierProviderRef> { } class _AlbumActivityProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AlbumActivityRef { + extends + AutoDisposeAsyncNotifierProviderElement> + with AlbumActivityRef { _AlbumActivityProviderElement(super.provider); @override @@ -207,5 +189,6 @@ class _AlbumActivityProviderElement @override String? get assetId => (origin as AlbumActivityProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/activity_statistics.provider.g.dart b/mobile/lib/providers/activity_statistics.provider.g.dart index d2de32c0aa..83d887f6dc 100644 --- a/mobile/lib/providers/activity_statistics.provider.g.dart +++ b/mobile/lib/providers/activity_statistics.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$ActivityStatistics extends BuildlessAutoDisposeNotifier { late final String albumId; late final String? assetId; - int build( - String albumId, [ - String? assetId, - ]); + int build(String albumId, [String? assetId]); } /// Maintains the current number of comments by @@ -58,24 +55,15 @@ class ActivityStatisticsFamily extends Family { /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider call( - String albumId, [ - String? assetId, - ]) { - return ActivityStatisticsProvider( - albumId, - assetId, - ); + ActivityStatisticsProvider call(String albumId, [String? assetId]) { + return ActivityStatisticsProvider(albumId, assetId); } @override ActivityStatisticsProvider getProviderOverride( covariant ActivityStatisticsProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -101,25 +89,22 @@ class ActivityStatisticsProvider /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => ActivityStatistics() - ..albumId = albumId - ..assetId = assetId, - from: activityStatisticsProvider, - name: r'activityStatisticsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityStatisticsHash, - dependencies: ActivityStatisticsFamily._dependencies, - allTransitiveDependencies: - ActivityStatisticsFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + ActivityStatisticsProvider(String albumId, [String? assetId]) + : this._internal( + () => ActivityStatistics() + ..albumId = albumId + ..assetId = assetId, + from: activityStatisticsProvider, + name: r'activityStatisticsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activityStatisticsHash, + dependencies: ActivityStatisticsFamily._dependencies, + allTransitiveDependencies: + ActivityStatisticsFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); ActivityStatisticsProvider._internal( super._createNotifier, { @@ -136,13 +121,8 @@ class ActivityStatisticsProvider final String? assetId; @override - int runNotifierBuild( - covariant ActivityStatistics notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + int runNotifierBuild(covariant ActivityStatistics notifier) { + return notifier.build(albumId, assetId); } @override @@ -206,5 +186,6 @@ class _ActivityStatisticsProviderElement @override String? get assetId => (origin as ActivityStatisticsProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index ae565d20d8..35634d77c8 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -35,31 +35,15 @@ class AlbumNotifier extends StateNotifier> { Future deleteAlbum(Album album) => albumService.deleteAlbum(album); - Future createAlbum( - String albumTitle, - Set assets, - ) => - albumService.createAlbum(albumTitle, assets, []); + Future createAlbum(String albumTitle, Set assets) => albumService.createAlbum(albumTitle, assets, []); - Future getAlbumByName( - String albumName, { - bool? remote, - bool? shared, - bool? owner, - }) => - albumService.getAlbumByName( - albumName, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String albumName, {bool? remote, bool? shared, bool? owner}) => + albumService.getAlbumByName(albumName, remote: remote, shared: shared, owner: owner); /// Create an album on the server with the same name as the selected album for backup /// First this will check if the album already exists on the server with name /// If it does not exist, it will create the album on the server - Future createSyncAlbum( - String albumName, - ) async { + Future createSyncAlbum(String albumName) async { final album = await getAlbumByName(albumName, remote: true, owner: true); if (album != null) { return; @@ -105,10 +89,7 @@ class AlbumNotifier extends StateNotifier> { return await albumService.removeAsset(album, assets); } - Future setActivitystatus( - Album album, - bool enabled, - ) { + Future setActivitystatus(Album album, bool enabled) { return albumService.setActivityStatus(album, enabled); } @@ -126,10 +107,7 @@ class AlbumNotifier extends StateNotifier> { } final albumProvider = StateNotifierProvider.autoDispose>((ref) { - return AlbumNotifier( - ref.watch(albumServiceProvider), - ref, - ); + return AlbumNotifier(ref.watch(albumServiceProvider), ref); }); final albumWatcher = StreamProvider.autoDispose.family((ref, id) async* { diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.dart b/mobile/lib/providers/album/album_sort_by_options.provider.dart index 6e1669faef..3dd09f1282 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.dart @@ -75,22 +75,10 @@ class _AlbumSortHandlers { enum AlbumSortMode { title(1, "library_page_sort_title", _AlbumSortHandlers.title), assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount), - lastModified( - 3, - "library_page_sort_last_modified", - _AlbumSortHandlers.lastModified, - ), + lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified), created(0, "library_page_sort_created", _AlbumSortHandlers.created), - mostRecent( - 2, - "sort_recent", - _AlbumSortHandlers.mostRecent, - ), - mostOldest( - 5, - "sort_oldest", - _AlbumSortHandlers.mostOldest, - ); + mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent), + mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest); final int storeIndex; final String label; @@ -104,18 +92,12 @@ class AlbumSortByOptions extends _$AlbumSortByOptions { @override AlbumSortMode build() { final sortOpt = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortOrder); - return AlbumSortMode.values.firstWhere( - (e) => e.storeIndex == sortOpt, - orElse: () => AlbumSortMode.title, - ); + return AlbumSortMode.values.firstWhere((e) => e.storeIndex == sortOpt, orElse: () => AlbumSortMode.title); } void changeSortMode(AlbumSortMode sortOption) { state = sortOption; - ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - sortOption.storeIndex, - ); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortOrder, sortOption.storeIndex); } } diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart index ba20e7eb66..750329c9d5 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart @@ -13,14 +13,14 @@ String _$albumSortByOptionsHash() => @ProviderFor(AlbumSortByOptions) final albumSortByOptionsProvider = AutoDisposeNotifierProvider.internal( - AlbumSortByOptions.new, - name: r'albumSortByOptionsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortByOptionsHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortByOptions.new, + name: r'albumSortByOptionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortByOptionsHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortByOptions = AutoDisposeNotifier; String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @@ -29,14 +29,14 @@ String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @ProviderFor(AlbumSortOrder) final albumSortOrderProvider = AutoDisposeNotifierProvider.internal( - AlbumSortOrder.new, - name: r'albumSortOrderProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortOrderHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortOrder.new, + name: r'albumSortOrderProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortOrderHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortOrder = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/album_title.provider.dart b/mobile/lib/providers/album/album_title.provider.dart index 126b3499a3..bf812a01d8 100644 --- a/mobile/lib/providers/album/album_title.provider.dart +++ b/mobile/lib/providers/album/album_title.provider.dart @@ -12,6 +12,4 @@ class AlbumTitleNotifier extends StateNotifier { } } -final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier(), -); +final albumTitleProvider = StateNotifierProvider((ref) => AlbumTitleNotifier()); diff --git a/mobile/lib/providers/album/album_viewer.provider.dart b/mobile/lib/providers/album/album_viewer.provider.dart index afae154cd7..f4ce047464 100644 --- a/mobile/lib/providers/album/album_viewer.provider.dart +++ b/mobile/lib/providers/album/album_viewer.provider.dart @@ -5,13 +5,7 @@ import 'package:immich_mobile/services/album.service.dart'; class AlbumViewerNotifier extends StateNotifier { AlbumViewerNotifier(this.ref) - : super( - const AlbumViewerPageState( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ), - ); + : super(const AlbumViewerPageState(editTitleText: "", isEditAlbum: false, editDescriptionText: "")); final Ref ref; @@ -40,17 +34,10 @@ class AlbumViewerNotifier extends StateNotifier { } void resetState() { - state = state.copyWith( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ); + state = state.copyWith(editTitleText: "", isEditAlbum: false, editDescriptionText: ""); } - Future changeAlbumTitle( - Album album, - String newAlbumTitle, - ) async { + Future changeAlbumTitle(Album album, String newAlbumTitle) async { AlbumService service = ref.watch(albumServiceProvider); bool isSuccess = await service.changeTitleAlbum(album, newAlbumTitle); @@ -65,16 +52,10 @@ class AlbumViewerNotifier extends StateNotifier { return false; } - Future changeAlbumDescription( - Album album, - String newAlbumDescription, - ) async { + Future changeAlbumDescription(Album album, String newAlbumDescription) async { AlbumService service = ref.watch(albumServiceProvider); - bool isSuccess = await service.changeDescriptionAlbum( - album, - newAlbumDescription, - ); + bool isSuccess = await service.changeDescriptionAlbum(album, newAlbumDescription); if (isSuccess) { state = state.copyWith(editDescriptionText: "", isEditAlbum: false); diff --git a/mobile/lib/providers/album/current_album.provider.g.dart b/mobile/lib/providers/album/current_album.provider.g.dart index 60ebe3e333..b6d079231f 100644 --- a/mobile/lib/providers/album/current_album.provider.g.dart +++ b/mobile/lib/providers/album/current_album.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAlbumHash() => r'61f00273d6b69da45add1532cc3d3a076ee55110'; @ProviderFor(CurrentAlbum) final currentAlbumProvider = AutoDisposeNotifierProvider.internal( - CurrentAlbum.new, - name: r'currentAlbumProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAlbumHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAlbum.new, + name: r'currentAlbumProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAlbumHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAlbum = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/api.provider.g.dart b/mobile/lib/providers/api.provider.g.dart index 8a6f514ce6..ee1781c24c 100644 --- a/mobile/lib/providers/api.provider.g.dart +++ b/mobile/lib/providers/api.provider.g.dart @@ -13,8 +13,9 @@ String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c'; final apiServiceProvider = Provider.internal( apiService, name: r'apiServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$apiServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$apiServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 07778ed2ed..31342bf23e 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -27,14 +27,7 @@ import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; -enum AppLifeCycleEnum { - active, - inactive, - paused, - resumed, - detached, - hidden, -} +enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden } class AppLifeCycleNotifier extends StateNotifier { final Ref _ref; @@ -93,15 +86,13 @@ class AppLifeCycleNotifier extends StateNotifier { // Ensure proper cleanup before starting new background tasks try { await Future.wait([ - backgroundManager.syncLocal().then( - (_) { - Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); - // Check if app is still active before hashing - if (state == AppLifeCycleEnum.resumed) { - backgroundManager.hashAssets(); - } - }, - ), + backgroundManager.syncLocal().then((_) { + Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); + // Check if app is still active before hashing + if (state == AppLifeCycleEnum.resumed) { + backgroundManager.hashAssets(); + } + }), backgroundManager.syncRemote(), ]).then((_) async { final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); @@ -116,11 +107,7 @@ class AppLifeCycleNotifier extends StateNotifier { } }); } catch (e, stackTrace) { - Logger("AppLifeCycleNotifier").severe( - "Error during background sync", - e, - stackTrace, - ); + Logger("AppLifeCycleNotifier").severe("Error during background sync", e, stackTrace); } } diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 5098dd2af4..9c8b28e6cf 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -68,9 +68,7 @@ class AssetNotifier extends StateNotifier { } final bool newRemote = await _assetService.refreshRemoteAssets(); final bool newLocal = await _albumService.refreshDeviceAlbums(); - debugPrint( - "changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal", - ); + debugPrint("changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal"); if (newRemote) { _ref.invalidate(memoryFutureProvider); } @@ -122,17 +120,11 @@ class AssetNotifier extends StateNotifier { /// Delete remote asset only /// /// Default behavior is trashing the asset - Future deleteRemoteAssets( - Iterable deleteAssets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable deleteAssets, {bool shouldDeletePermanently = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteRemoteAssets( - deleteAssets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await _assetService.deleteRemoteAssets(deleteAssets, shouldDeletePermanently: shouldDeletePermanently); return true; } catch (error) { log.severe("Failed to delete remote assets", error); @@ -143,17 +135,11 @@ class AssetNotifier extends StateNotifier { } } - Future deleteAssets( - Iterable deleteAssets, { - bool force = false, - }) async { + Future deleteAssets(Iterable deleteAssets, {bool force = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteAssets( - deleteAssets, - shouldDeletePermanently: force, - ); + await _assetService.deleteAssets(deleteAssets, shouldDeletePermanently: force); return true; } catch (error) { log.severe("Failed to delete assets", error); @@ -174,10 +160,7 @@ class AssetNotifier extends StateNotifier { return _assetService.changeArchiveStatus(assets, status); } - Future setLockedView( - List selection, - AssetVisibilityEnum visibility, - ) { + Future setLockedView(List selection, AssetVisibilityEnum visibility) { return _assetService.setVisibility(selection, visibility); } } diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart index ebe8a14186..031a70e0d9 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart @@ -30,13 +30,12 @@ class _SystemHash { } } -abstract class _$AssetPeopleNotifier extends BuildlessAutoDisposeAsyncNotifier< - List> { +abstract class _$AssetPeopleNotifier + extends + BuildlessAutoDisposeAsyncNotifier> { late final Asset asset; - FutureOr> build( - Asset asset, - ); + FutureOr> build(Asset asset); } /// Maintains the list of people for an asset. @@ -58,21 +57,15 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider call( - Asset asset, - ) { - return AssetPeopleNotifierProvider( - asset, - ); + AssetPeopleNotifierProvider call(Asset asset) { + return AssetPeopleNotifierProvider(asset); } @override AssetPeopleNotifierProvider getProviderOverride( covariant AssetPeopleNotifierProvider provider, ) { - return call( - provider.asset, - ); + return call(provider.asset); } static const Iterable? _dependencies = null; @@ -93,26 +86,28 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. -class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< - AssetPeopleNotifier, List> { +class AssetPeopleNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + AssetPeopleNotifier, + List + > { /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider( - Asset asset, - ) : this._internal( - () => AssetPeopleNotifier()..asset = asset, - from: assetPeopleNotifierProvider, - name: r'assetPeopleNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$assetPeopleNotifierHash, - dependencies: AssetPeopleNotifierFamily._dependencies, - allTransitiveDependencies: - AssetPeopleNotifierFamily._allTransitiveDependencies, - asset: asset, - ); + AssetPeopleNotifierProvider(Asset asset) + : this._internal( + () => AssetPeopleNotifier()..asset = asset, + from: assetPeopleNotifierProvider, + name: r'assetPeopleNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$assetPeopleNotifierHash, + dependencies: AssetPeopleNotifierFamily._dependencies, + allTransitiveDependencies: + AssetPeopleNotifierFamily._allTransitiveDependencies, + asset: asset, + ); AssetPeopleNotifierProvider._internal( super._createNotifier, { @@ -130,9 +125,7 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< FutureOr> runNotifierBuild( covariant AssetPeopleNotifier notifier, ) { - return notifier.build( - asset, - ); + return notifier.build(asset); } @override @@ -152,8 +145,11 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< } @override - AutoDisposeAsyncNotifierProviderElement> createElement() { + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + createElement() { return _AssetPeopleNotifierProviderElement(this); } @@ -180,12 +176,17 @@ mixin AssetPeopleNotifierRef } class _AssetPeopleNotifierProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AssetPeopleNotifierRef { + extends + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + with AssetPeopleNotifierRef { _AssetPeopleNotifierProviderElement(super.provider); @override Asset get asset => (origin as AssetPeopleNotifierProvider).asset; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart index 53b02c2ace..e0d8d47d3a 100644 --- a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAssetHash() => r'2def10ea594152c984ae2974d687ab6856d7bdd0'; @ProviderFor(CurrentAsset) final currentAssetProvider = AutoDisposeNotifierProvider.internal( - CurrentAsset.new, - name: r'currentAssetProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAssetHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAsset.new, + name: r'currentAssetProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAssetHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAsset = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index b35a4546bb..36b935abe7 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -18,17 +18,14 @@ class DownloadStateNotifier extends StateNotifier { final ShareService _shareService; final AlbumService _albumService; - DownloadStateNotifier( - this._downloadService, - this._shareService, - this._albumService, - ) : super( - const DownloadState( - downloadStatus: TaskStatus.complete, - showProgress: false, - taskProgress: {}, - ), - ) { + DownloadStateNotifier(this._downloadService, this._shareService, this._albumService) + : super( + const DownloadState( + downloadStatus: TaskStatus.complete, + showProgress: false, + taskProgress: {}, + ), + ) { _downloadService.onImageDownloadStatus = _downloadImageCallback; _downloadService.onVideoDownloadStatus = _downloadVideoCallback; _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; @@ -131,9 +128,7 @@ class DownloadStateNotifier extends StateNotifier { ); if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } _albumService.refreshDeviceAlbums(); }); @@ -159,9 +154,7 @@ class DownloadStateNotifier extends StateNotifier { } if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } } @@ -169,19 +162,17 @@ class DownloadStateNotifier extends StateNotifier { showDialog( context: context, builder: (BuildContext buildContext) { - _shareService.shareAsset(asset, context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + _shareService.shareAsset(asset, context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -192,8 +183,8 @@ class DownloadStateNotifier extends StateNotifier { final downloadStateProvider = StateNotifierProvider( ((ref) => DownloadStateNotifier( - ref.watch(downloadServiceProvider), - ref.watch(shareServiceProvider), - ref.watch(albumServiceProvider), - )), + ref.watch(downloadServiceProvider), + ref.watch(shareServiceProvider), + ref.watch(albumServiceProvider), + )), ); diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index 97730b5935..7b2ab5b27a 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -15,10 +15,10 @@ import 'package:path/path.dart'; final shareIntentUploadProvider = StateNotifierProvider>( ((ref) => ShareIntentUploadStateNotifier( - ref.watch(appRouterProvider), - ref.watch(uploadServiceProvider), - ref.watch(shareIntentServiceProvider), - )), + ref.watch(appRouterProvider), + ref.watch(uploadServiceProvider), + ref.watch(shareIntentServiceProvider), + )), ); class ShareIntentUploadStateNotifier extends StateNotifier> { @@ -26,11 +26,7 @@ class ShareIntentUploadStateNotifier extends StateNotifier UploadStatus.running, TaskStatus.paused => UploadStatus.paused, TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingToRetry + TaskStatus.waitingToRetry => UploadStatus.waitingToRetry, }; state = [ @@ -106,19 +102,12 @@ class ShareIntentUploadStateNotifier extends StateNotifier upload(File file) async { - final task = await _buildUploadTask( - hash(file.path).toString(), - file, - ); + final task = await _buildUploadTask(hash(file.path).toString(), file); _uploadService.enqueueTasks([task]); } - Future _buildUploadTask( - String id, - File file, { - Map? fields, - }) async { + Future _buildUploadTask(String id, File file, {Map? fields}) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); diff --git a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart index a758d97ad8..3cfc2e2f6f 100644 --- a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; class VideoPlaybackControls { - const VideoPlaybackControls({ - required this.position, - required this.pause, - this.restarted = false, - }); + const VideoPlaybackControls({required this.position, required this.pause, this.restarted = false}); final double position; final bool pause; @@ -67,9 +63,9 @@ class VideoPlayerControls extends StateNotifier { void restart() { state = const VideoPlaybackControls(position: 0, pause: false, restarted: true); - ref.read(videoPlaybackValueProvider.notifier).value = ref.read(videoPlaybackValueProvider.notifier).value.copyWith( - state: VideoPlaybackState.playing, - position: Duration.zero, - ); + ref.read(videoPlaybackValueProvider.notifier).value = ref + .read(videoPlaybackValueProvider.notifier) + .value + .copyWith(state: VideoPlaybackState.playing, position: Duration.zero); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart index 55a381b02b..c478ddd6f5 100644 --- a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart @@ -1,13 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:native_video_player/native_video_player.dart'; -enum VideoPlaybackState { - initializing, - paused, - playing, - buffering, - completed, -} +enum VideoPlaybackState { initializing, paused, playing, buffering, completed } class VideoPlaybackValue { /// The current position of the video @@ -22,16 +16,9 @@ class VideoPlaybackValue { /// The volume of the video final double volume; - const VideoPlaybackValue({ - required this.position, - required this.duration, - required this.state, - required this.volume, - }); + const VideoPlaybackValue({required this.position, required this.duration, required this.state, required this.volume}); - factory VideoPlaybackValue.fromNativeController( - NativeVideoPlayerController controller, - ) { + factory VideoPlaybackValue.fromNativeController(NativeVideoPlayerController controller) { final playbackInfo = controller.playbackInfo; final videoInfo = controller.videoInfo; @@ -53,12 +40,7 @@ class VideoPlaybackValue { ); } - VideoPlaybackValue copyWith({ - Duration? position, - Duration? duration, - VideoPlaybackState? state, - double? volume, - }) { + VideoPlaybackValue copyWith({Duration? position, Duration? duration, VideoPlaybackState? state, double? volume}) { return VideoPlaybackValue( position: position ?? this.position, duration: duration ?? this.duration, @@ -92,22 +74,12 @@ class VideoPlaybackValueState extends StateNotifier { set position(Duration value) { if (state.position == value) return; - state = VideoPlaybackValue( - position: value, - duration: state.duration, - state: state.state, - volume: state.volume, - ); + state = VideoPlaybackValue(position: value, duration: state.duration, state: state.state, volume: state.volume); } set status(VideoPlaybackState value) { if (state.state == value) return; - state = VideoPlaybackValue( - position: state.position, - duration: state.duration, - state: value, - volume: state.volume, - ); + state = VideoPlaybackValue(position: state.position, duration: state.duration, state: value, volume: state.volume); } void reset() { diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index b918eeb215..fc3e08472b 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -49,16 +49,16 @@ class AuthNotifier extends StateNotifier { this._secureStorageService, this._widgetService, ) : super( - const AuthState( - deviceId: "", - userId: "", - userEmail: "", - name: '', - profileImagePath: '', - isAdmin: false, - isAuthenticated: false, - ), - ); + const AuthState( + deviceId: "", + userId: "", + userEmail: "", + name: '', + profileImagePath: '', + isAdmin: false, + isAuthenticated: false, + ), + ); Future validateServerUrl(String url) { return _authService.validateServerUrl(url); @@ -118,15 +118,10 @@ class AuthNotifier extends StateNotifier { } } - Future saveAuthInfo({ - required String accessToken, - }) async { + Future saveAuthInfo({required String accessToken}) async { await _apiService.setAccessToken(accessToken); - await _widgetService.writeCredentials( - Store.get(StoreKey.serverEndpoint), - accessToken, - ); + await _widgetService.writeCredentials(Store.get(StoreKey.serverEndpoint), accessToken); // Get the deviceid from the store if it exists, otherwise generate a new one String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; @@ -150,21 +145,12 @@ class AuthNotifier extends StateNotifier { _log.severe("Unauthorized access, token likely expired. Logging out."); return false; } - _log.severe( - "Error getting user information from the server [API EXCEPTION]", - stackTrace, - ); + _log.severe("Error getting user information from the server [API EXCEPTION]", stackTrace); } catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [CATCH ALL]", - error, - stackTrace, - ); + _log.severe("Error getting user information from the server [CATCH ALL]", error, stackTrace); if (kDebugMode) { - debugPrint( - "Error getting user information from the server [CATCH ALL] $error $stackTrace", - ); + debugPrint("Error getting user information from the server [CATCH ALL] $error $stackTrace"); } } diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 69290bcfc5..76cb383465 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -60,43 +60,38 @@ class BackupNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - BackUpState( - backupProgress: BackUpProgressEnum.idle, - allAssetsInDatabase: const [], - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - autoBackup: Store.get(StoreKey.autoBackup, false), - backgroundBackup: Store.get(StoreKey.backgroundBackup, false), - backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), - backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), - backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), - serverInfo: const ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - availableAlbums: const [], - selectedBackupAlbums: const {}, - excludedBackupAlbums: const {}, - allUniqueAssets: const {}, - selectedAlbumsBackupAssetsIds: const {}, - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - fileSize: 0, - iCloudAsset: false, - ), - iCloudDownloadProgress: 0.0, + BackUpState( + backupProgress: BackUpProgressEnum.idle, + allAssetsInDatabase: const [], + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + autoBackup: Store.get(StoreKey.autoBackup, false), + backgroundBackup: Store.get(StoreKey.backgroundBackup, false), + backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), + backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), + backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), + serverInfo: const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + availableAlbums: const [], + selectedBackupAlbums: const {}, + excludedBackupAlbums: const {}, + allUniqueAssets: const {}, + selectedAlbumsBackupAssetsIds: const {}, + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', + fileSize: 0, + iCloudAsset: false, ), - ); + iCloudDownloadProgress: 0.0, + ), + ); final log = Logger('BackupNotifier'); final BackupService _backupService; @@ -153,11 +148,7 @@ class BackupNotifier extends StateNotifier { // disable any backup cancelBackup(); setAutoBackup(false); - configureBackgroundBackup( - enabled: false, - onError: (msg) {}, - onBatteryInfo: () {}, - ); + configureBackgroundBackup(enabled: false, onError: (msg) {}, onBatteryInfo: () {}); } return _updateBackupAssetCount(); } @@ -175,9 +166,7 @@ class BackupNotifier extends StateNotifier { required void Function(String msg) onError, required void Function() onBatteryInfo, }) async { - assert( - enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null, - ); + assert(enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null); final bool wasEnabled = state.backgroundBackup; final bool wasWifi = state.backupRequireWifi; final bool wasCharging = state.backupRequireCharging; @@ -197,7 +186,8 @@ class BackupNotifier extends StateNotifier { } success &= await _backgroundService.enableService(immediate: true); } - success &= success && + success &= + success && await _backgroundService.configureService( requireUnmetered: state.backupRequireWifi, requireCharging: state.backupRequireCharging, @@ -206,10 +196,7 @@ class BackupNotifier extends StateNotifier { ); if (success) { await Store.put(StoreKey.backupRequireWifi, state.backupRequireWifi); - await Store.put( - StoreKey.backupRequireCharging, - state.backupRequireCharging, - ); + await Store.put(StoreKey.backupRequireCharging, state.backupRequireCharging); await Store.put(StoreKey.backupTriggerDelay, state.backupTriggerDelay); await Store.put(StoreKey.backgroundBackup, state.backgroundBackup); } else { @@ -296,14 +283,9 @@ class BackupNotifier extends StateNotifier { } } - state = state.copyWith( - selectedBackupAlbums: selectedAlbums, - excludedBackupAlbums: excludedAlbums, - ); + state = state.copyWith(selectedBackupAlbums: selectedAlbums, excludedBackupAlbums: excludedAlbums); - log.info( - "_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums", - ); + log.info("_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums"); debugPrint("_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms"); } @@ -333,21 +315,14 @@ class BackupNotifier extends StateNotifier { for (final asset in assets) { List albumNames = [album.name]; - final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull( - (a) => a.asset.localId == asset.localId, - ); + final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull((a) => a.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); assetsFromSelectedAlbums.remove(existingAsset); } - assetsFromSelectedAlbums.add( - BackupCandidate( - asset: asset, - albumNames: albumNames, - ), - ); + assetsFromSelectedAlbums.add(BackupCandidate(asset: asset, albumNames: albumNames)); } } @@ -361,9 +336,7 @@ class BackupNotifier extends StateNotifier { final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); for (final asset in assets) { - assetsFromExcludedAlbums.add( - BackupCandidate(asset: asset, albumNames: [album.name]), - ); + assetsFromExcludedAlbums.add(BackupCandidate(asset: asset, albumNames: [album.name])); } } @@ -381,9 +354,7 @@ class BackupNotifier extends StateNotifier { selectedAlbumsBackupAssets.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); // Remove duplicated asset from all unique assets - allUniqueAssets.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + allUniqueAssets.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (allUniqueAssets.isEmpty) { log.info("No assets are selected for back up"); @@ -509,9 +480,7 @@ class BackupNotifier extends StateNotifier { } void setAvailableAlbums(availableAlbums) { - state = state.copyWith( - availableAlbums: availableAlbums, - ); + state = state.copyWith(availableAlbums: availableAlbums); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { @@ -541,28 +510,20 @@ class BackupNotifier extends StateNotifier { if (result.isDuplicate) { state = state.copyWith( allUniqueAssets: state.allUniqueAssets - .where( - (candidate) => candidate.asset.localId != result.candidate.asset.localId, - ) + .where((candidate) => candidate.asset.localId != result.candidate.asset.localId) .toSet(), ); } else { state = state.copyWith( - selectedAlbumsBackupAssetsIds: { - ...state.selectedAlbumsBackupAssetsIds, - result.candidate.asset.localId!, - }, - allAssetsInDatabase: [ - ...state.allAssetsInDatabase, - result.candidate.asset.localId!, - ], + selectedAlbumsBackupAssetsIds: {...state.selectedAlbumsBackupAssetsIds, result.candidate.asset.localId!}, + allAssetsInDatabase: [...state.allAssetsInDatabase, result.candidate.asset.localId!], ); } if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { - final latestAssetBackup = state.allUniqueAssets.map((candidate) => candidate.asset.fileModifiedAt).reduce( - (v, e) => e.isAfter(v) ? e : v, - ); + final latestAssetBackup = state.allUniqueAssets + .map((candidate) => candidate.asset.fileModifiedAt) + .reduce((v, e) => e.isAfter(v) ? e : v); state = state.copyWith( selectedBackupAlbums: state.selectedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), excludedBackupAlbums: state.excludedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), @@ -594,9 +555,7 @@ class BackupNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -618,9 +577,7 @@ class BackupNotifier extends StateNotifier { // Update server info if (diskInfo != null) { - state = state.copyWith( - serverInfo: diskInfo, - ); + state = state.copyWith(serverInfo: diskInfo); } } @@ -665,17 +622,11 @@ class BackupNotifier extends StateNotifier { Set selectedAlbums = state.selectedBackupAlbums; Set excludedAlbums = state.excludedBackupAlbums; if (selectedAlbums.isNotEmpty) { - selectedAlbums = _updateAlbumsBackupTime( - selectedAlbums, - selectedBackupAlbums, - ); + selectedAlbums = _updateAlbumsBackupTime(selectedAlbums, selectedBackupAlbums); } if (excludedAlbums.isNotEmpty) { - excludedAlbums = _updateAlbumsBackupTime( - excludedAlbums, - excludedBackupAlbums, - ); + excludedAlbums = _updateAlbumsBackupTime(excludedAlbums, excludedBackupAlbums); } final BackUpProgressEnum previous = state.backupProgress; state = state.copyWith( @@ -692,32 +643,21 @@ class BackupNotifier extends StateNotifier { return _resumeBackup(); } - Set _updateAlbumsBackupTime( - Set albums, - List backupAlbums, - ) { + Set _updateAlbumsBackupTime(Set albums, List backupAlbums) { Set result = {}; for (BackupAlbum ba in backupAlbums) { try { AvailableAlbum a = albums.firstWhere((e) => e.id == ba.id); result.add(a.copyWith(lastBackup: ba.lastBackup)); } on StateError { - log.severe( - "[_updateAlbumBackupTime] failed to find album in state", - "State Error", - StackTrace.current, - ); + log.severe("[_updateAlbumBackupTime] failed to find album in state", "State Error", StackTrace.current); } } return result; } Future notifyBackgroundServiceCanRun() async { - const allowedStates = [ - AppLifeCycleEnum.inactive, - AppLifeCycleEnum.paused, - AppLifeCycleEnum.detached, - ]; + const allowedStates = [AppLifeCycleEnum.inactive, AppLifeCycleEnum.paused, AppLifeCycleEnum.detached]; if (allowedStates.contains(ref.read(appStateProvider.notifier).state)) { _backgroundService.releaseLock(); } diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart index 1e5d424926..f81f905c2f 100644 --- a/mobile/lib/providers/backup/backup_album.provider.dart +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; final backupAlbumProvider = StateNotifierProvider>( - (ref) => BackupAlbumNotifier( - ref.watch(localAlbumServiceProvider), - ), + (ref) => BackupAlbumNotifier(ref.watch(localAlbumServiceProvider)), ); class BackupAlbumNotifier extends StateNotifier> { diff --git a/mobile/lib/providers/backup/backup_verification.provider.dart b/mobile/lib/providers/backup/backup_verification.provider.dart index c075d81d86..da4253576b 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.dart @@ -64,7 +64,8 @@ class BackupVerification extends _$BackupVerification { onOk: () => _performDeletion(context, toDelete), title: "Corrupt backups!", ok: "Delete", - content: "Found ${toDelete.length} (max $limit at once) corrupt asset backups. " + content: + "Found ${toDelete.length} (max $limit at once) corrupt asset backups. " "Run the check again to find more.\n" "Do you want to delete the corrupt asset backups now?", ), @@ -77,23 +78,18 @@ class BackupVerification extends _$BackupVerification { } } - Future _performDeletion( - BuildContext context, - List assets, - ) async { + Future _performDeletion(BuildContext context, List assets) async { try { state = true; if (context.mounted) { - ImmichToast.show( - context: context, - msg: "Deleting ${assets.length} assets on the server...", - ); + ImmichToast.show(context: context, msg: "Deleting ${assets.length} assets on the server..."); } await ref.read(assetProvider.notifier).deleteAssets(assets, force: true); if (context.mounted) { ImmichToast.show( context: context, - msg: "Deleted ${assets.length} assets on the server. " + msg: + "Deleted ${assets.length} assets on the server. " "You can now start a manual backup", toastType: ToastType.success, ); diff --git a/mobile/lib/providers/backup/backup_verification.provider.g.dart b/mobile/lib/providers/backup/backup_verification.provider.g.dart index bae3ec366b..727e06a12c 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.g.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.g.dart @@ -13,14 +13,14 @@ String _$backupVerificationHash() => @ProviderFor(BackupVerification) final backupVerificationProvider = AutoDisposeNotifierProvider.internal( - BackupVerification.new, - name: r'backupVerificationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$backupVerificationHash, - dependencies: null, - allTransitiveDependencies: null, -); + BackupVerification.new, + name: r'backupVerificationProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$backupVerificationHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$BackupVerification = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index d352869651..aca10e60a7 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -14,19 +14,10 @@ class EnqueueStatus { final int enqueueCount; final int totalCount; - const EnqueueStatus({ - required this.enqueueCount, - required this.totalCount, - }); + const EnqueueStatus({required this.enqueueCount, required this.totalCount}); - EnqueueStatus copyWith({ - int? enqueueCount, - int? totalCount, - }) { - return EnqueueStatus( - enqueueCount: enqueueCount ?? this.enqueueCount, - totalCount: totalCount ?? this.totalCount, - ); + EnqueueStatus copyWith({int? enqueueCount, int? totalCount}) { + return EnqueueStatus(enqueueCount: enqueueCount ?? this.enqueueCount, totalCount: totalCount ?? this.totalCount); } @override @@ -197,25 +188,22 @@ class DriftBackupState { } final driftBackupProvider = StateNotifierProvider((ref) { - return ExpBackupNotifier( - ref.watch(uploadServiceProvider), - ); + return ExpBackupNotifier(ref.watch(uploadServiceProvider)); }); class ExpBackupNotifier extends StateNotifier { - ExpBackupNotifier( - this._uploadService, - ) : super( - const DriftBackupState( - totalCount: 0, - backupCount: 0, - remainderCount: 0, - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: false, - uploadItems: {}, - ), - ) { + ExpBackupNotifier(this._uploadService) + : super( + const DriftBackupState( + totalCount: 0, + backupCount: 0, + remainderCount: 0, + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: false, + uploadItems: {}, + ), + ) { { _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); _uploadService.taskProgressStream.listen(_handleTaskProgressUpdate); @@ -241,10 +229,7 @@ class ExpBackupNotifier extends StateNotifier { switch (update.status) { case TaskStatus.complete: if (update.task.group == kBackupGroup) { - state = state.copyWith( - backupCount: state.backupCount + 1, - remainderCount: state.remainderCount - 1, - ); + state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1); } // Remove the completed task from the upload items @@ -260,14 +245,7 @@ class ExpBackupNotifier extends StateNotifier { return; } - state = state.copyWith( - uploadItems: { - ...state.uploadItems, - taskId: currentItem.copyWith( - isFailed: true, - ), - }, - ); + state = state.copyWith(uploadItems: {...state.uploadItems, taskId: currentItem.copyWith(isFailed: true)}); break; case TaskStatus.canceled: @@ -299,9 +277,7 @@ class ExpBackupNotifier extends StateNotifier { fileSize: update.expectedFileSize, networkSpeedAsString: update.networkSpeedAsString, ) - : currentItem.copyWith( - progress: progress, - ), + : currentItem.copyWith(progress: progress), }, ); @@ -329,11 +305,7 @@ class ExpBackupNotifier extends StateNotifier { _uploadService.getBackupRemainderCount(userId), ]); - state = state.copyWith( - totalCount: totalCount, - backupCount: backupCount, - remainderCount: remainderCount, - ); + state = state.copyWith(totalCount: totalCount, backupCount: backupCount, remainderCount: remainderCount); } Future startBackup(String userId) { @@ -341,34 +313,22 @@ class ExpBackupNotifier extends StateNotifier { } void _updateEnqueueCount(EnqueueStatus status) { - state = state.copyWith( - enqueueCount: status.enqueueCount, - enqueueTotalCount: status.totalCount, - ); + state = state.copyWith(enqueueCount: status.enqueueCount, enqueueTotalCount: status.totalCount); } Future cancel() async { debugPrint("Canceling backup tasks..."); - state = state.copyWith( - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: true, - ); + state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true); final activeTaskCount = await _uploadService.cancelBackup(); if (activeTaskCount > 0) { - debugPrint( - "$activeTaskCount tasks left, continuing to cancel...", - ); + debugPrint("$activeTaskCount tasks left, continuing to cancel..."); await cancel(); } else { debugPrint("All tasks canceled successfully."); // Clear all upload items when cancellation is complete - state = state.copyWith( - isCanceling: false, - uploadItems: {}, - ); + state = state.copyWith(isCanceling: false, uploadItems: {}); } } diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index b234a4ffe2..1aea7f64cb 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -56,44 +56,43 @@ class ManualUploadNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - ManualUploadState( - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - ), - totalAssetsToUpload: 0, - successfulUploads: 0, - currentAssetIndex: 0, - showDetailedNotification: false, + ManualUploadState( + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', ), - ); + totalAssetsToUpload: 0, + successfulUploads: 0, + currentAssetIndex: 0, + showDetailedNotification: false, + ), + ); String _lastPrintedDetailContent = ''; String? _lastPrintedDetailTitle; static const notifyInterval = Duration(milliseconds: 500); late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); void _updateProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { _localNotificationService.showOrUpdateManualUploadStatus( "backup_background_service_in_progress_notification".tr(), - formatAssetBackupProgress( - state.currentAssetIndex, - state.totalAssetsToUpload, - ), + formatAssetBackupProgress(state.currentAssetIndex, state.totalAssetsToUpload), maxProgress: state.totalAssetsToUpload, progress: state.currentAssetIndex, showActions: true, @@ -146,9 +145,7 @@ class ManualUploadNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -165,23 +162,22 @@ class ManualUploadNotifier extends StateNotifier { ); if (state.showDetailedNotification) { - final title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': state.currentUploadAsset.fileName}); + final title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': state.currentUploadAsset.fileName}, + ); _throttledDetailNotify(title: title, progress: sent, total: total); } } void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset) { - state = state.copyWith( - currentUploadAsset: currentUploadAsset, - currentAssetIndex: state.currentAssetIndex + 1, - ); + state = state.copyWith(currentUploadAsset: currentUploadAsset, currentAssetIndex: state.currentAssetIndex + 1); if (state.totalAssetsToUpload > 1) { _throttledNotifiy(); } if (state.showDetailedNotification) { - _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } @@ -216,10 +212,7 @@ class ManualUploadNotifier extends StateNotifier { // Extrack candidate from allAssetsFromDevice final uploadAssets = candidates.where( (candidate) => - allAssetsFromDevice.firstWhereOrNull( - (asset) => asset.localId == candidate.asset.localId, - ) != - null, + allAssetsFromDevice.firstWhereOrNull((asset) => asset.localId == candidate.asset.localId) != null, ); if (uploadAssets.isEmpty) { @@ -251,14 +244,15 @@ class ManualUploadNotifier extends StateNotifier { } // Show detailed asset if enabled in settings or if a single asset is uploaded - bool showDetailedNotification = ref.read(appSettingsServiceProvider).getSetting( - AppSettingsEnum.backgroundBackupSingleProgress, - ) || + bool showDetailedNotification = + ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.backgroundBackupSingleProgress) || state.totalAssetsToUpload == 1; state = state.copyWith(showDetailedNotification: showDetailedNotification); final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; - final bool ok = await ref.read(backupServiceProvider).backupAsset( + final bool ok = await ref + .read(backupServiceProvider) + .backupAsset( uploadAssets, state.cancelToken, pmProgressHandler: pmProgressHandler, @@ -269,9 +263,7 @@ class ManualUploadNotifier extends StateNotifier { ); // Close detailed notification - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); _log.info( '[_startUpload] Manual Upload Completed - success: ${state.successfulUploads},' @@ -310,9 +302,7 @@ class ManualUploadNotifier extends StateNotifier { } finally { _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); _handleAppInActivity(); - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); await _backupProvider.notifyBackgroundServiceCanRun(); } return !hasErrors; @@ -345,10 +335,7 @@ class ManualUploadNotifier extends StateNotifier { ); } - Future uploadAssets( - BuildContext context, - Iterable allManualUploads, - ) async { + Future uploadAssets(BuildContext context, Iterable allManualUploads) async { // assumes the background service is currently running and // waits until it has stopped to start the backup. final bool hasLock = await ref.read(backgroundServiceProvider).acquireLock(); diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index 11cdcd54c5..75a2a35fb6 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -15,15 +15,15 @@ class CastNotifier extends StateNotifier { List<(String, CastDestinationType, dynamic)> discovered = List.empty(); CastNotifier(this._gCastService) - : super( - const CastManagerState( - isCasting: false, - currentTime: Duration.zero, - duration: Duration.zero, - receiverName: '', - castState: CastState.idle, - ), - ) { + : super( + const CastManagerState( + isCasting: false, + currentTime: Duration.zero, + duration: Duration.zero, + receiverName: '', + castState: CastState.idle, + ), + ) { _gCastService.onConnectionState = _onConnectionState; _gCastService.onCurrentTime = _onCurrentTime; _gCastService.onDuration = _onDuration; @@ -65,8 +65,8 @@ class CastNotifier extends StateNotifier { type: asset.type == old_asset_entity.AssetType.image ? AssetType.image : asset.type == old_asset_entity.AssetType.video - ? AssetType.video - : AssetType.other, + ? AssetType.video + : AssetType.other, createdAt: asset.fileCreatedAt, updatedAt: asset.updatedAt, ); diff --git a/mobile/lib/providers/folder.provider.dart b/mobile/lib/providers/folder.provider.dart index 7f89679734..696d7e19fd 100644 --- a/mobile/lib/providers/folder.provider.dart +++ b/mobile/lib/providers/folder.provider.dart @@ -23,9 +23,7 @@ class FolderStructureNotifier extends StateNotifier> { } final folderStructureProvider = StateNotifierProvider>((ref) { - return FolderStructureNotifier( - ref.watch(folderServiceProvider), - ); + return FolderStructureNotifier(ref.watch(folderServiceProvider)); }); class FolderRenderListNotifier extends StateNotifier> { @@ -49,8 +47,5 @@ class FolderRenderListNotifier extends StateNotifier> { final folderRenderListProvider = StateNotifierProvider.family, RootFolder>((ref, folder) { - return FolderRenderListNotifier( - ref.watch(folderServiceProvider), - folder, - ); -}); + return FolderRenderListNotifier(ref.watch(folderServiceProvider), folder); + }); diff --git a/mobile/lib/providers/gallery_permission.provider.dart b/mobile/lib/providers/gallery_permission.provider.dart index 3d8a6da941..6e4fc69926 100644 --- a/mobile/lib/providers/gallery_permission.provider.dart +++ b/mobile/lib/providers/gallery_permission.provider.dart @@ -5,8 +5,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:permission_handler/permission_handler.dart'; class GalleryPermissionNotifier extends StateNotifier { - GalleryPermissionNotifier() : super(PermissionStatus.denied) // Denied is the initial state - { + GalleryPermissionNotifier() + : super(PermissionStatus.denied) // Denied is the initial state + { // Sets the initial state getGalleryPermissionStatus(); } diff --git a/mobile/lib/providers/image/cache/image_loader.dart b/mobile/lib/providers/image/cache/image_loader.dart index f88d54e4f1..50530f7cdf 100644 --- a/mobile/lib/providers/image/cache/image_loader.dart +++ b/mobile/lib/providers/image/cache/image_loader.dart @@ -19,20 +19,13 @@ class ImageLoader { }) async { final headers = ApiService.getRequestHeaders(); - final stream = cache.getFileStream( - uri, - withProgress: chunkEvents != null, - headers: headers, - ); + final stream = cache.getFileStream(uri, withProgress: chunkEvents != null, headers: headers); await for (final result in stream) { if (result is DownloadProgress) { // We are downloading the file, so update the [chunkEvents] chunkEvents?.add( - ImageChunkEvent( - cumulativeBytesLoaded: result.downloaded, - expectedTotalBytes: result.totalSize, - ), + ImageChunkEvent(cumulativeBytesLoaded: result.downloaded, expectedTotalBytes: result.totalSize), ); } else if (result is FileInfo) { // We have the file diff --git a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart index da20f46c62..b9e2880c04 100644 --- a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart @@ -9,12 +9,5 @@ class RemoteImageCacheManager extends CacheManager { return _instance; } - RemoteImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 500, - stalePeriod: const Duration(days: 30), - ), - ); + RemoteImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 500, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart index f8d4cda3e6..bfea36eef6 100644 --- a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart @@ -9,12 +9,5 @@ class ThumbnailImageCacheManager extends CacheManager { return _instance; } - ThumbnailImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 5000, - stalePeriod: const Duration(days: 30), - ), - ); + ThumbnailImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 5000, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 4c77ee4b56..8c46c52906 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -18,11 +18,8 @@ class ImmichLocalImageProvider extends ImageProvider { final double height; final Logger log = Logger('ImmichLocalImageProvider'); - ImmichLocalImageProvider({ - required this.asset, - required this.width, - required this.height, - }) : assert(asset.local != null, 'Only usable when asset.local is set'); + ImmichLocalImageProvider({required this.asset, required this.width, required this.height}) + : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @@ -32,10 +29,7 @@ class ImmichLocalImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ImmichLocalImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalImageProvider key, ImageDecoderCallback decode) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( codec: _codec(key.asset, decode, chunkEvents), diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index de69115444..5edb0fc79e 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -32,17 +32,12 @@ class ImmichLocalThumbnailProvider extends ImageProvider obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichLocalThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( codec: _codec(key.asset, cache, decode), @@ -54,11 +49,7 @@ class ImmichLocalThumbnailProvider extends ImageProvider _codec( - Asset assetData, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(Asset assetData, CacheManager cache, ImageDecoderCallback decode) async* { final cacheKey = '$userId${assetData.localId}${assetData.checksum}$width$height'; final fileFromCache = await cache.getFileFromCache(cacheKey); if (fileFromCache != null) { @@ -72,14 +63,9 @@ class ImmichLocalThumbnailProvider extends ImageProvider /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteImageProvider({ - required this.assetId, - this.cacheManager, - }); + const ImmichRemoteImageProvider({required this.assetId, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( @@ -51,10 +43,7 @@ class ImmichRemoteImageProvider extends ImageProvider } /// Whether to show the original file or load a compressed version - bool get _useOriginal => Store.get( - AppSettingsEnum.loadOriginal.storeKey, - AppSettingsEnum.loadOriginal.defaultValue, - ); + bool get _useOriginal => Store.get(AppSettingsEnum.loadOriginal.storeKey, AppSettingsEnum.loadOriginal.defaultValue); // Streams in each stage of the image as we ask for it Stream _codec( @@ -64,28 +53,15 @@ class ImmichRemoteImageProvider extends ImageProvider StreamController chunkEvents, ) async* { // Load the higher resolution version of the image - final url = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.preview, - ); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final url = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.preview); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; // Load the final remote image if (_useOriginal) { // Load the original image final url = getOriginalUrlForRemoteId(key.assetId); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; } await chunkEvents.close(); diff --git a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart index cb2a6270b4..08ee4325e8 100644 --- a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart @@ -23,51 +23,27 @@ class ImmichRemoteThumbnailProvider extends ImageProvider obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode), - scale: 1.0, - ); + return MultiImageStreamCompleter(codec: _codec(key, cache, decode), scale: 1.0); } // Streams in each stage of the image as we ask for it - Stream _codec( - ImmichRemoteThumbnailProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(ImmichRemoteThumbnailProvider key, CacheManager cache, ImageDecoderCallback decode) async* { // Load a preview to the chunk events - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.thumbnail); - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - ); + yield await ImageLoader.loadImageFromCache(preview, cache: cache, decode: decode); } @override diff --git a/mobile/lib/providers/immich_logo_provider.g.dart b/mobile/lib/providers/immich_logo_provider.g.dart index 90b117d574..f1af433c1b 100644 --- a/mobile/lib/providers/immich_logo_provider.g.dart +++ b/mobile/lib/providers/immich_logo_provider.g.dart @@ -13,8 +13,9 @@ String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea'; final immichLogoProvider = AutoDisposeFutureProvider.internal( immichLogo, name: r'immichLogoProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$immichLogoHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$immichLogoHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 26de1b4dba..9d05a6ecab 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -12,10 +12,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; final actionProvider = NotifierProvider( ActionNotifier.new, - dependencies: [ - multiSelectProvider, - timelineServiceProvider, - ], + dependencies: [multiSelectProvider, timelineServiceProvider], ); class ActionResult { @@ -74,37 +71,31 @@ class ActionNotifier extends Notifier { Iterable _getIdsForSource(ActionSource source) { final Set assets = _getAssets(source); return switch (T) { - const (RemoteAsset) => assets.whereType(), - const (LocalAsset) => assets.whereType(), - _ => const [], - } as Iterable; + const (RemoteAsset) => assets.whereType(), + const (LocalAsset) => assets.whereType(), + _ => const [], + } + as Iterable; } Set _getAssets(ActionSource source) { return switch (source) { ActionSource.timeline => ref.read(multiSelectProvider).selectedAssets, ActionSource.viewer => switch (ref.read(currentAssetNotifier)) { - BaseAsset asset => {asset}, - null => const {}, - }, + BaseAsset asset => {asset}, + null => const {}, + }, }; } - Future shareLink( - ActionSource source, - BuildContext context, - ) async { + Future shareLink(ActionSource source, BuildContext context) async { final ids = _getRemoteIdsForSource(source); try { await _service.shareLink(ids, context); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to create shared link for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -115,11 +106,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to favorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -130,11 +117,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unfavorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -145,11 +128,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to archive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -160,11 +139,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unarchive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -176,11 +151,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to move assets to lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -191,11 +162,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -207,11 +174,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -222,11 +185,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to restore trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -238,11 +197,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -254,11 +209,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -269,18 +220,11 @@ class ActionNotifier extends Notifier { return ActionResult(count: deletedCount, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future editLocation( - ActionSource source, - BuildContext context, - ) async { + Future editLocation(ActionSource source, BuildContext context) async { final ids = _getOwnedRemoteIdsForSource(source); try { final isEdited = await _service.editLocation(ids, context); @@ -291,29 +235,18 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to edit location for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future removeFromAlbum( - ActionSource source, - String albumId, - ) async { + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { final removedCount = await _service.removeFromAlbum(ids, albumId); return ActionResult(count: removedCount, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from album', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -324,11 +257,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to stack assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -339,10 +268,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed to unstack assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - ); + return ActionResult(count: assets.length, success: false); } } @@ -354,11 +280,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: count, success: true); } catch (error, stack) { _logger.severe('Failed to share assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -371,11 +293,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: enqueueCount, success: true); } catch (error, stack) { _logger.severe('Failed to download assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } @@ -386,11 +304,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed manually upload assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } } diff --git a/mobile/lib/providers/infrastructure/album.provider.dart b/mobile/lib/providers/infrastructure/album.provider.dart index 4baead4b75..da0f9bc9ce 100644 --- a/mobile/lib/providers/infrastructure/album.provider.dart +++ b/mobile/lib/providers/infrastructure/album.provider.dart @@ -30,10 +30,7 @@ final remoteAlbumRepository = Provider( ); final remoteAlbumServiceProvider = Provider( - (ref) => RemoteAlbumService( - ref.watch(remoteAlbumRepository), - ref.watch(driftAlbumApiRepositoryProvider), - ), + (ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)), dependencies: [remoteAlbumRepository], ); diff --git a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart index 66de676c08..1956170c1e 100644 --- a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -final currentAssetNotifier = AutoDisposeNotifierProvider( - CurrentAssetNotifier.new, -); +final currentAssetNotifier = AutoDisposeNotifierProvider(CurrentAssetNotifier.new); class CurrentAssetNotifier extends AutoDisposeNotifier { KeepAliveLink? _keepAliveLink; @@ -33,12 +31,10 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { } } -final currentAssetExifProvider = FutureProvider.autoDispose( - (ref) { - final currentAsset = ref.watch(currentAssetNotifier); - if (currentAsset == null) { - return null; - } - return ref.watch(assetServiceProvider).getExif(currentAsset); - }, -); +final currentAssetExifProvider = FutureProvider.autoDispose((ref) { + final currentAsset = ref.watch(currentAssetNotifier); + if (currentAsset == null) { + return null; + } + return ref.watch(assetServiceProvider).getExif(currentAsset); +}); diff --git a/mobile/lib/providers/infrastructure/db.provider.g.dart b/mobile/lib/providers/infrastructure/db.provider.g.dart index 33b330192f..46abfb66a9 100644 --- a/mobile/lib/providers/infrastructure/db.provider.g.dart +++ b/mobile/lib/providers/infrastructure/db.provider.g.dart @@ -13,8 +13,9 @@ String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb'; final isarProvider = Provider.internal( isar, name: r'isarProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$isarHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$isarHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart index a733104b33..ac8a457e3a 100644 --- a/mobile/lib/providers/infrastructure/person.provider.dart +++ b/mobile/lib/providers/infrastructure/person.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftPersonProvider = Provider( - (ref) => DriftPersonRepository(ref.watch(driftProvider)), -); +final driftPersonProvider = Provider((ref) => DriftPersonRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index c6d9337b53..ca7735808c 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -15,19 +15,11 @@ class RemoteAlbumState { final List albums; final List filteredAlbums; - const RemoteAlbumState({ - required this.albums, - List? filteredAlbums, - }) : filteredAlbums = filteredAlbums ?? albums; + const RemoteAlbumState({required this.albums, List? filteredAlbums}) + : filteredAlbums = filteredAlbums ?? albums; - RemoteAlbumState copyWith({ - List? albums, - List? filteredAlbums, - }) { - return RemoteAlbumState( - albums: albums ?? this.albums, - filteredAlbums: filteredAlbums ?? this.filteredAlbums, - ); + RemoteAlbumState copyWith({List? albums, List? filteredAlbums}) { + return RemoteAlbumState(albums: albums ?? this.albums, filteredAlbums: filteredAlbums ?? this.filteredAlbums); } @override @@ -57,10 +49,7 @@ class RemoteAlbumNotifier extends Notifier { Future> _getAll() async { try { final albums = await _remoteAlbumService.getAll(); - state = state.copyWith( - albums: albums, - filteredAlbums: albums, - ); + state = state.copyWith(albums: albums, filteredAlbums: albums); return albums; } catch (error, stack) { _logger.severe('Failed to fetch albums', error, stack); @@ -72,33 +61,17 @@ class RemoteAlbumNotifier extends Notifier { await _getAll(); } - void searchAlbums( - String query, - String? userId, [ - QuickFilterMode filterMode = QuickFilterMode.all, - ]) { - final filtered = _remoteAlbumService.searchAlbums( - state.albums, - query, - userId, - filterMode, - ); + void searchAlbums(String query, String? userId, [QuickFilterMode filterMode = QuickFilterMode.all]) { + final filtered = _remoteAlbumService.searchAlbums(state.albums, query, userId, filterMode); - state = state.copyWith( - filteredAlbums: filtered, - ); + state = state.copyWith(filteredAlbums: filtered); } void clearSearch() { - state = state.copyWith( - filteredAlbums: state.albums, - ); + state = state.copyWith(filteredAlbums: state.albums); } - void sortFilteredAlbums( - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { + void sortFilteredAlbums(RemoteAlbumSortMode sortMode, {bool isReverse = false}) { final sortedAlbums = _remoteAlbumService.sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); state = state.copyWith(filteredAlbums: sortedAlbums); } @@ -109,16 +82,9 @@ class RemoteAlbumNotifier extends Notifier { List assetIds = const [], }) async { try { - final album = await _remoteAlbumService.createAlbum( - title: title, - description: description, - assetIds: assetIds, - ); + final album = await _remoteAlbumService.createAlbum(title: title, description: description, assetIds: assetIds); - state = state.copyWith( - albums: [...state.albums, album], - filteredAlbums: [...state.filteredAlbums, album], - ); + state = state.copyWith(albums: [...state.albums, album], filteredAlbums: [...state.filteredAlbums, album]); return album; } catch (error, stack) { @@ -153,10 +119,7 @@ class RemoteAlbumNotifier extends Notifier { return album.id == albumId ? updatedAlbum : album; }).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); return updatedAlbum; } catch (error, stack) { @@ -179,10 +142,7 @@ class RemoteAlbumNotifier extends Notifier { final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); final updatedFilteredAlbums = state.filteredAlbums.where((album) => album.id != albumId).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); } Future> getAssets(String albumId) { @@ -190,32 +150,22 @@ class RemoteAlbumNotifier extends Notifier { } Future addAssets(String albumId, List assetIds) { - return _remoteAlbumService.addAssets( - albumId: albumId, - assetIds: assetIds, - ); + return _remoteAlbumService.addAssets(albumId: albumId, assetIds: assetIds); } Future addUsers(String albumId, List userIds) { - return _remoteAlbumService.addUsers( - albumId: albumId, - userIds: userIds, - ); + return _remoteAlbumService.addUsers(albumId: albumId, userIds: userIds); } } -final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>( - (ref, albumId) async { - final service = ref.watch(remoteAlbumServiceProvider); - return service.getDateRange(albumId); - }, -); +final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>((ref, albumId) async { + final service = ref.watch(remoteAlbumServiceProvider); + return service.getDateRange(albumId); +}); -final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>( - (ref, albumId) async { - final link = ref.keepAlive(); - ref.onDispose(() => link.close()); - final service = ref.watch(remoteAlbumServiceProvider); - return service.getSharedUsers(albumId); - }, -); +final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>((ref, albumId) async { + final link = ref.keepAlive(); + ref.onDispose(() => link.close()); + final service = ref.watch(remoteAlbumServiceProvider); + return service.getSharedUsers(albumId); +}); diff --git a/mobile/lib/providers/infrastructure/search.provider.dart b/mobile/lib/providers/infrastructure/search.provider.dart index cdcd3ee43b..7d992f9d5f 100644 --- a/mobile/lib/providers/infrastructure/search.provider.dart +++ b/mobile/lib/providers/infrastructure/search.provider.dart @@ -3,10 +3,6 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final searchApiRepositoryProvider = Provider( - (ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi), -); +final searchApiRepositoryProvider = Provider((ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi)); -final searchServiceProvider = Provider( - (ref) => SearchService(ref.watch(searchApiRepositoryProvider)), -); +final searchServiceProvider = Provider((ref) => SearchService(ref.watch(searchApiRepositoryProvider))); diff --git a/mobile/lib/providers/infrastructure/stack.provider.dart b/mobile/lib/providers/infrastructure/stack.provider.dart index 71abd1e87a..0528fd0c91 100644 --- a/mobile/lib/providers/infrastructure/stack.provider.dart +++ b/mobile/lib/providers/infrastructure/stack.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftStackProvider = Provider( - (ref) => DriftStackRepository(ref.watch(driftProvider)), -); +final driftStackProvider = Provider((ref) => DriftStackRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/storage.provider.dart b/mobile/lib/providers/infrastructure/storage.provider.dart index 5bbbe51497..ccca964027 100644 --- a/mobile/lib/providers/infrastructure/storage.provider.dart +++ b/mobile/lib/providers/infrastructure/storage.provider.dart @@ -1,6 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -final storageRepositoryProvider = Provider( - (ref) => const StorageRepository(), -); +final storageRepositoryProvider = Provider((ref) => const StorageRepository()); diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 22b783013a..98c978cb60 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -30,8 +30,9 @@ String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0'; final storeServiceProvider = Provider.internal( storeService, name: r'storeServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$storeServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 2406c37fa8..ddc6eed441 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -20,13 +20,9 @@ final syncStreamServiceProvider = Provider( ), ); -final syncApiRepositoryProvider = Provider( - (ref) => SyncApiRepository(ref.watch(apiServiceProvider)), -); +final syncApiRepositoryProvider = Provider((ref) => SyncApiRepository(ref.watch(apiServiceProvider))); -final syncStreamRepositoryProvider = Provider( - (ref) => SyncStreamRepository(ref.watch(driftProvider)), -); +final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.watch(driftProvider))); final localSyncServiceProvider = Provider( (ref) => LocalSyncService( diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index 1f8c344f31..06ec0242b2 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -33,13 +33,11 @@ final timelineFactoryProvider = Provider( ), ); -final timelineUsersProvider = StreamProvider>( - (ref) { - final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); - if (currentUserId == null) { - return Stream.value([]); - } +final timelineUsersProvider = StreamProvider>((ref) { + final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); + if (currentUserId == null) { + return Stream.value([]); + } - return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); - }, -); + return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); +}); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index cd62be2bec..922b9866bb 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -22,10 +22,10 @@ UserApiRepository userApiRepository(Ref ref) => UserApiRepository(ref.watch(apiS @Riverpod(keepAlive: true) UserService userService(Ref ref) => UserService( - isarUserRepository: ref.watch(userRepositoryProvider), - userApiRepository: ref.watch(userApiRepositoryProvider), - storeService: ref.watch(storeServiceProvider), - ); + isarUserRepository: ref.watch(userRepositoryProvider), + userApiRepository: ref.watch(userApiRepositoryProvider), + storeService: ref.watch(storeServiceProvider), +); /// Drifts final driftPartnerRepositoryProvider = Provider( @@ -33,12 +33,7 @@ final driftPartnerRepositoryProvider = Provider( ); final driftPartnerServiceProvider = Provider( - (ref) => DriftPartnerService( - ref.watch(driftPartnerRepositoryProvider), - ref.watch(partnerApiRepositoryProvider), - ), + (ref) => DriftPartnerService(ref.watch(driftPartnerRepositoryProvider), ref.watch(partnerApiRepositoryProvider)), ); -final partnerUsersProvider = NotifierProvider>( - PartnerNotifier.new, -); +final partnerUsersProvider = NotifierProvider>(PartnerNotifier.new); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart index 7664b15fd5..f9148bf3a7 100644 --- a/mobile/lib/providers/infrastructure/user.provider.g.dart +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -47,8 +47,9 @@ String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1'; final userServiceProvider = Provider.internal( userService, name: r'userServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$userServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$userServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/local_auth.provider.dart b/mobile/lib/providers/local_auth.provider.dart index 56a5b2191b..44fc5ad80c 100644 --- a/mobile/lib/providers/local_auth.provider.dart +++ b/mobile/lib/providers/local_auth.provider.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:logging/logging.dart'; final localAuthProvider = StateNotifierProvider((ref) { - return LocalAuthNotifier( - ref.watch(localAuthServiceProvider), - ref.watch(secureStorageServiceProvider), - ); + return LocalAuthNotifier(ref.watch(localAuthServiceProvider), ref.watch(secureStorageServiceProvider)); }); class LocalAuthNotifier extends StateNotifier { @@ -23,17 +20,9 @@ class LocalAuthNotifier extends StateNotifier { final _log = Logger("LocalAuthNotifier"); LocalAuthNotifier(this._localAuthService, this._secureStorageService) - : super( - const BiometricStatus( - availableBiometrics: [], - canAuthenticate: false, - ), - ) { + : super(const BiometricStatus(availableBiometrics: [], canAuthenticate: false)) { _localAuthService.getStatus().then((value) { - state = state.copyWith( - canAuthenticate: value.canAuthenticate, - availableBiometrics: value.availableBiometrics, - ); + state = state.copyWith(canAuthenticate: value.canAuthenticate, availableBiometrics: value.availableBiometrics); }); } @@ -79,10 +68,7 @@ class LocalAuthNotifier extends StateNotifier { if (errorMessage.isNotEmpty) { context.showSnackBar( SnackBar( - content: Text( - errorMessage, - style: context.textTheme.labelLarge, - ), + content: Text(errorMessage, style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.errorContainer, ), diff --git a/mobile/lib/providers/map/map_marker.provider.g.dart b/mobile/lib/providers/map/map_marker.provider.g.dart index a4c1db7dc0..80a21a39b2 100644 --- a/mobile/lib/providers/map/map_marker.provider.g.dart +++ b/mobile/lib/providers/map/map_marker.provider.g.dart @@ -13,8 +13,9 @@ String _$mapMarkersHash() => r'a0c129fcddbf1b9bce4aafcd2e47a858ab6ef1c9'; final mapMarkersProvider = AutoDisposeFutureProvider>.internal( mapMarkers, name: r'mapMarkersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapMarkersHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapMarkersHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_service.provider.g.dart b/mobile/lib/providers/map/map_service.provider.g.dart index 0bb5094c61..e8eb1cd1ee 100644 --- a/mobile/lib/providers/map/map_service.provider.g.dart +++ b/mobile/lib/providers/map/map_service.provider.g.dart @@ -13,8 +13,9 @@ String _$mapServiceHash() => r'ffc8f38b726083452b9df236ed58903879348987'; final mapServiceProvider = AutoDisposeProvider.internal( mapService, name: r'mapServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index 4337654be0..31f2849df6 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -28,22 +28,13 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchTheme(ThemeMode mode) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapThemeMode, - mode.index, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapThemeMode, mode.index); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapShowFavoriteOnly, - isFavoriteOnly, - ); - state = state.copyWith( - showFavoriteOnly: isFavoriteOnly, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } void setRefetchMarkers(bool shouldRefetch) { @@ -51,35 +42,17 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapIncludeArchived, - isIncludeArchived, - ); - state = state.copyWith( - includeArchived: isIncludeArchived, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapwithPartners, - isWithPartners, - ); - state = state.copyWith( - withPartners: isWithPartners, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapRelativeDate, - relativeTime, - ); - state = state.copyWith( - relativeTime: relativeTime, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeTime); + state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/providers/map/map_state.provider.g.dart b/mobile/lib/providers/map/map_state.provider.g.dart index 85a237099c..94d0ff8698 100644 --- a/mobile/lib/providers/map/map_state.provider.g.dart +++ b/mobile/lib/providers/map/map_state.provider.g.dart @@ -12,14 +12,14 @@ String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66'; @ProviderFor(MapStateNotifier) final mapStateNotifierProvider = NotifierProvider.internal( - MapStateNotifier.new, - name: r'mapStateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$mapStateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); + MapStateNotifier.new, + name: r'mapStateNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapStateNotifierHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$MapStateNotifier = Notifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/network.provider.dart b/mobile/lib/providers/network.provider.dart index 5cb2fae4b1..cd91ff6d56 100644 --- a/mobile/lib/providers/network.provider.dart +++ b/mobile/lib/providers/network.provider.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/network.service.dart'; final networkProvider = StateNotifierProvider((ref) { - return NetworkNotifier( - ref.watch(networkServiceProvider), - ); + return NetworkNotifier(ref.watch(networkServiceProvider)); }); class NetworkNotifier extends StateNotifier { diff --git a/mobile/lib/providers/notification_permission.provider.dart b/mobile/lib/providers/notification_permission.provider.dart index e293452390..da0badd4ec 100644 --- a/mobile/lib/providers/notification_permission.provider.dart +++ b/mobile/lib/providers/notification_permission.provider.dart @@ -5,9 +5,7 @@ import 'package:permission_handler/permission_handler.dart'; class NotificationPermissionNotifier extends StateNotifier { NotificationPermissionNotifier() - : super( - Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted, - ) { + : super(Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted) { // Sets the initial state getNotificationPermission().then((p) => state = p); } diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index 37e07958d3..5a85cea1d4 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -12,17 +12,20 @@ class PartnerSharedWithNotifier extends StateNotifier> { PartnerSharedWithNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedWith().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedWith().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedWith() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedWith().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } Future updatePartner(UserDto partner, {required bool inTimeline}) { @@ -39,9 +42,7 @@ class PartnerSharedWithNotifier extends StateNotifier> { } final partnerSharedWithProvider = StateNotifierProvider>((ref) { - return PartnerSharedWithNotifier( - ref.watch(partnerServiceProvider), - ); + return PartnerSharedWithNotifier(ref.watch(partnerServiceProvider)); }); class PartnerSharedByNotifier extends StateNotifier> { @@ -50,17 +51,20 @@ class PartnerSharedByNotifier extends StateNotifier> { PartnerSharedByNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedBy().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedBy().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedBy() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedBy().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } @override diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index dbeacb45c6..9a37d83320 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -28,10 +28,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } @@ -42,13 +39,8 @@ class PaginatedSearchNotifier extends StateNotifier { } @riverpod -Future paginatedSearchRenderList( - Ref ref, -) { +Future paginatedSearchRenderList(Ref ref) { final result = ref.watch(paginatedSearchProvider); final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - result.assets, - GroupAssetsBy.none, - ); + return timelineService.getTimelineFromAssets(result.assets, GroupAssetsBy.none); } diff --git a/mobile/lib/providers/search/paginated_search.provider.g.dart b/mobile/lib/providers/search/paginated_search.provider.g.dart index 650cf130fc..e984997967 100644 --- a/mobile/lib/providers/search/paginated_search.provider.g.dart +++ b/mobile/lib/providers/search/paginated_search.provider.g.dart @@ -13,14 +13,14 @@ String _$paginatedSearchRenderListHash() => @ProviderFor(paginatedSearchRenderList) final paginatedSearchRenderListProvider = AutoDisposeFutureProvider.internal( - paginatedSearchRenderList, - name: r'paginatedSearchRenderListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$paginatedSearchRenderListHash, - dependencies: null, - allTransitiveDependencies: null, -); + paginatedSearchRenderList, + name: r'paginatedSearchRenderListProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchRenderListHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index f0faabd35a..3ff8d67983 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -9,9 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'people.provider.g.dart'; @riverpod -Future> getAllPeople( - Ref ref, -) async { +Future> getAllPeople(Ref ref) async { final PersonService personService = ref.read(personServiceProvider); final people = await personService.getAllPeople(); @@ -30,11 +28,7 @@ Future personAssets(Ref ref, String personId) async { } @riverpod -Future updatePersonName( - Ref ref, - String personId, - String updatedName, -) async { +Future updatePersonName(Ref ref, String personId, String updatedName) async { final PersonService personService = ref.read(personServiceProvider); final person = await personService.updateName(personId, updatedName); diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart index 4625891abb..9595c36eec 100644 --- a/mobile/lib/providers/search/people.provider.g.dart +++ b/mobile/lib/providers/search/people.provider.g.dart @@ -12,13 +12,14 @@ String _$getAllPeopleHash() => r'2c5e6a207683f15ab209650615fdf9cb7f76c736'; @ProviderFor(getAllPeople) final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( - getAllPeople, - name: r'getAllPeopleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$getAllPeopleHash, - dependencies: null, - allTransitiveDependencies: null, -); + getAllPeople, + name: r'getAllPeopleProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getAllPeopleHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element @@ -56,21 +57,15 @@ class PersonAssetsFamily extends Family> { const PersonAssetsFamily(); /// See also [personAssets]. - PersonAssetsProvider call( - String personId, - ) { - return PersonAssetsProvider( - personId, - ); + PersonAssetsProvider call(String personId) { + return PersonAssetsProvider(personId); } @override PersonAssetsProvider getProviderOverride( covariant PersonAssetsProvider provider, ) { - return call( - provider.personId, - ); + return call(provider.personId); } static const Iterable? _dependencies = null; @@ -91,24 +86,19 @@ class PersonAssetsFamily extends Family> { /// See also [personAssets]. class PersonAssetsProvider extends AutoDisposeFutureProvider { /// See also [personAssets]. - PersonAssetsProvider( - String personId, - ) : this._internal( - (ref) => personAssets( - ref as PersonAssetsRef, - personId, - ), - from: personAssetsProvider, - name: r'personAssetsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$personAssetsHash, - dependencies: PersonAssetsFamily._dependencies, - allTransitiveDependencies: - PersonAssetsFamily._allTransitiveDependencies, - personId: personId, - ); + PersonAssetsProvider(String personId) + : this._internal( + (ref) => personAssets(ref as PersonAssetsRef, personId), + from: personAssetsProvider, + name: r'personAssetsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$personAssetsHash, + dependencies: PersonAssetsFamily._dependencies, + allTransitiveDependencies: + PersonAssetsFamily._allTransitiveDependencies, + personId: personId, + ); PersonAssetsProvider._internal( super._createNotifier, { @@ -167,7 +157,8 @@ mixin PersonAssetsRef on AutoDisposeFutureProviderRef { } class _PersonAssetsProviderElement - extends AutoDisposeFutureProviderElement with PersonAssetsRef { + extends AutoDisposeFutureProviderElement + with PersonAssetsRef { _PersonAssetsProviderElement(super.provider); @override @@ -186,24 +177,15 @@ class UpdatePersonNameFamily extends Family> { const UpdatePersonNameFamily(); /// See also [updatePersonName]. - UpdatePersonNameProvider call( - String personId, - String updatedName, - ) { - return UpdatePersonNameProvider( - personId, - updatedName, - ); + UpdatePersonNameProvider call(String personId, String updatedName) { + return UpdatePersonNameProvider(personId, updatedName); } @override UpdatePersonNameProvider getProviderOverride( covariant UpdatePersonNameProvider provider, ) { - return call( - provider.personId, - provider.updatedName, - ); + return call(provider.personId, provider.updatedName); } static const Iterable? _dependencies = null; @@ -224,27 +206,21 @@ class UpdatePersonNameFamily extends Family> { /// See also [updatePersonName]. class UpdatePersonNameProvider extends AutoDisposeFutureProvider { /// See also [updatePersonName]. - UpdatePersonNameProvider( - String personId, - String updatedName, - ) : this._internal( - (ref) => updatePersonName( - ref as UpdatePersonNameRef, - personId, - updatedName, - ), - from: updatePersonNameProvider, - name: r'updatePersonNameProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$updatePersonNameHash, - dependencies: UpdatePersonNameFamily._dependencies, - allTransitiveDependencies: - UpdatePersonNameFamily._allTransitiveDependencies, - personId: personId, - updatedName: updatedName, - ); + UpdatePersonNameProvider(String personId, String updatedName) + : this._internal( + (ref) => + updatePersonName(ref as UpdatePersonNameRef, personId, updatedName), + from: updatePersonNameProvider, + name: r'updatePersonNameProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$updatePersonNameHash, + dependencies: UpdatePersonNameFamily._dependencies, + allTransitiveDependencies: + UpdatePersonNameFamily._allTransitiveDependencies, + personId: personId, + updatedName: updatedName, + ); UpdatePersonNameProvider._internal( super._createNotifier, { @@ -312,7 +288,8 @@ mixin UpdatePersonNameRef on AutoDisposeFutureProviderRef { } class _UpdatePersonNameProviderElement - extends AutoDisposeFutureProviderElement with UpdatePersonNameRef { + extends AutoDisposeFutureProviderElement + with UpdatePersonNameRef { _UpdatePersonNameProviderElement(super.provider); @override @@ -320,5 +297,6 @@ class _UpdatePersonNameProviderElement @override String get updatedName => (origin as UpdatePersonNameProvider).updatedName; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_filter.provider.g.dart b/mobile/lib/providers/search/search_filter.provider.g.dart index 03f88b0332..5a322ca285 100644 --- a/mobile/lib/providers/search/search_filter.provider.g.dart +++ b/mobile/lib/providers/search/search_filter.provider.g.dart @@ -95,29 +95,28 @@ class GetSearchSuggestionsProvider String? make, String? model, }) : this._internal( - (ref) => getSearchSuggestions( - ref as GetSearchSuggestionsRef, - type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ), - from: getSearchSuggestionsProvider, - name: r'getSearchSuggestionsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$getSearchSuggestionsHash, - dependencies: GetSearchSuggestionsFamily._dependencies, - allTransitiveDependencies: - GetSearchSuggestionsFamily._allTransitiveDependencies, - type: type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ); + (ref) => getSearchSuggestions( + ref as GetSearchSuggestionsRef, + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + from: getSearchSuggestionsProvider, + name: r'getSearchSuggestionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getSearchSuggestionsHash, + dependencies: GetSearchSuggestionsFamily._dependencies, + allTransitiveDependencies: + GetSearchSuggestionsFamily._allTransitiveDependencies, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); GetSearchSuggestionsProvider._internal( super._createNotifier, { @@ -227,5 +226,6 @@ class _GetSearchSuggestionsProviderElement @override String? get model => (origin as GetSearchSuggestionsProvider).model; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart index 4bbceca383..23d5606922 100644 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ b/mobile/lib/providers/search/search_page_state.provider.dart @@ -14,14 +14,7 @@ final getPreviewPlacesProvider = FutureProvider.autoDispose data.fieldName == "exifInfo.city").items; - final curatedContent = locations - .map( - (l) => SearchCuratedContent( - label: l.value, - id: l.data.id, - ), - ) - .toList(); + final curatedContent = locations.map((l) => SearchCuratedContent(label: l.value, id: l.data.id)).toList(); return curatedContent; }); @@ -36,12 +29,7 @@ final getAllPlacesProvider = FutureProvider.autoDispose SearchCuratedContent( - label: data.exifInfo!.city!, - id: data.id, - ), - ) + .map((data) => SearchCuratedContent(label: data.exifInfo!.city!, id: data.id)) .toList(); return curatedContent; diff --git a/mobile/lib/providers/server_info.provider.dart b/mobile/lib/providers/server_info.provider.dart index 4a5e65878b..25b1002b7a 100644 --- a/mobile/lib/providers/server_info.provider.dart +++ b/mobile/lib/providers/server_info.provider.dart @@ -11,42 +11,24 @@ import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) - : super( - const ServerInfo( - serverVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - latestVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - serverFeatures: ServerFeatures( - map: true, - trash: true, - oauthEnabled: false, - passwordLogin: true, - ), - serverConfig: ServerConfig( - trashDays: 30, - oauthButtonText: '', - externalDomain: '', - mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', - mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', - ), - serverDiskInfo: ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - isVersionMismatch: false, - isNewReleaseAvailable: false, - versionMismatchErrorMessage: "", + : super( + const ServerInfo( + serverVersion: ServerVersion(major: 0, minor: 0, patch: 0), + latestVersion: ServerVersion(major: 0, minor: 0, patch: 0), + serverFeatures: ServerFeatures(map: true, trash: true, oauthEnabled: false, passwordLogin: true), + serverConfig: ServerConfig( + trashDays: 30, + oauthButtonText: '', + externalDomain: '', + mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', + mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', ), - ); + serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + isVersionMismatch: false, + isNewReleaseAvailable: false, + versionMismatchErrorMessage: "", + ), + ); final ServerInfoService _serverInfoService; final _log = Logger("ServerInfoNotifier"); @@ -62,19 +44,14 @@ class ServerInfoNotifier extends StateNotifier { final serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { - state = state.copyWith( - isVersionMismatch: true, - versionMismatchErrorMessage: "common_server_error".tr(), - ); + state = state.copyWith(isVersionMismatch: true, versionMismatchErrorMessage: "common_server_error".tr()); return; } await _checkServerVersionMismatch(serverVersion); } catch (e, stackTrace) { _log.severe("Failed to get server version", e, stackTrace); - state = state.copyWith( - isVersionMismatch: true, - ); + state = state.copyWith(isVersionMismatch: true); return; } } @@ -118,29 +95,21 @@ class ServerInfoNotifier extends StateNotifier { return; } - state = state.copyWith( - isVersionMismatch: false, - versionMismatchErrorMessage: "", - ); + state = state.copyWith(isVersionMismatch: false, versionMismatchErrorMessage: ""); } - handleNewRelease( - ServerVersion serverVersion, - ServerVersion latestVersion, - ) { + handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) { // Update local server version _checkServerVersionMismatch(serverVersion); final majorEqual = latestVersion.major == serverVersion.major; final minorEqual = majorEqual && latestVersion.minor == serverVersion.minor; - final newVersionAvailable = latestVersion.major > serverVersion.major || + final newVersionAvailable = + latestVersion.major > serverVersion.major || (majorEqual && latestVersion.minor > serverVersion.minor) || (minorEqual && latestVersion.patch > serverVersion.patch); - state = state.copyWith( - latestVersion: latestVersion, - isNewReleaseAvailable: newVersionAvailable, - ); + state = state.copyWith(latestVersion: latestVersion, isNewReleaseAvailable: newVersionAvailable); } getServerFeatures() async { @@ -166,11 +135,7 @@ class ServerInfoNotifier extends StateNotifier { var minor = detail[1]; var patch = detail[2]; - return { - "major": int.parse(major), - "minor": int.parse(minor), - "patch": int.parse(patch.replaceAll("-DEBUG", "")), - }; + return {"major": int.parse(major), "minor": int.parse(minor), "patch": int.parse(patch.replaceAll("-DEBUG", ""))}; } } diff --git a/mobile/lib/providers/shared_link.provider.dart b/mobile/lib/providers/shared_link.provider.dart index 32dfed51f2..f574554bcb 100644 --- a/mobile/lib/providers/shared_link.provider.dart +++ b/mobile/lib/providers/shared_link.provider.dart @@ -21,7 +21,5 @@ class SharedLinksNotifier extends StateNotifier>> { } final sharedLinksStateProvider = StateNotifierProvider>>((ref) { - return SharedLinksNotifier( - ref.watch(sharedLinkServiceProvider), - ); + return SharedLinksNotifier(ref.watch(sharedLinkServiceProvider)); }); diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index bf535f525d..8e24bbf4d0 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -12,7 +12,7 @@ enum SyncStatus { SyncStatus.idle => "idle".tr(), SyncStatus.syncing => "running".tr(), SyncStatus.success => "success".tr(), - SyncStatus.error => "error".tr() + SyncStatus.error => "error".tr(), }; } } @@ -60,12 +60,7 @@ class SyncStatusState { } @override - int get hashCode => Object.hash( - remoteSyncStatus, - localSyncStatus, - hashJobStatus, - errorMessage, - ); + int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage); } class SyncStatusNotifier extends Notifier { @@ -84,10 +79,7 @@ class SyncStatusNotifier extends Notifier { /// void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - remoteSyncStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(remoteSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startRemoteSync() => setRemoteSyncStatus(SyncStatus.syncing); @@ -99,10 +91,7 @@ class SyncStatusNotifier extends Notifier { /// void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - localSyncStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(localSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); @@ -114,10 +103,7 @@ class SyncStatusNotifier extends Notifier { /// void setHashJobStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - hashJobStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(hashJobStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startHashJob() => setHashJobStatus(SyncStatus.syncing); @@ -125,6 +111,4 @@ class SyncStatusNotifier extends Notifier { void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); } -final syncStatusProvider = NotifierProvider( - SyncStatusNotifier.new, -); +final syncStatusProvider = NotifierProvider(SyncStatusNotifier.new); diff --git a/mobile/lib/providers/tab.provider.dart b/mobile/lib/providers/tab.provider.dart index a4875115ce..d523e72c38 100644 --- a/mobile/lib/providers/tab.provider.dart +++ b/mobile/lib/providers/tab.provider.dart @@ -3,6 +3,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum TabEnum { home, search, albums, library } /// Provides the currently active tab -final tabProvider = StateProvider( - (ref) => TabEnum.home, -); +final tabProvider = StateProvider((ref) => TabEnum.home); diff --git a/mobile/lib/providers/theme.provider.dart b/mobile/lib/providers/theme.provider.dart index bdf3735f8e..5f32e07578 100644 --- a/mobile/lib/providers/theme.provider.dart +++ b/mobile/lib/providers/theme.provider.dart @@ -31,13 +31,8 @@ final immichThemePresetProvider = StateProvider((ref) { try { return ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorPreset); } catch (e) { - debugPrint( - "Theme preset $primaryColorPreset not found. Applying default preset.", - ); - appSettingsProvider.setSetting( - AppSettingsEnum.primaryColor, - defaultColorPresetName, - ); + debugPrint("Theme preset $primaryColorPreset not found. Applying default preset."); + appSettingsProvider.setSetting(AppSettingsEnum.primaryColor, defaultColorPresetName); return defaultColorPreset; } }); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index 6faccff030..71ea308dbf 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -5,27 +5,21 @@ import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -final singleUserTimelineProvider = StreamProvider.family( - (ref, userId) { - if (userId == null) { - return const Stream.empty(); - } +final singleUserTimelineProvider = StreamProvider.family((ref, userId) { + if (userId == null) { + return const Stream.empty(); + } - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchHomeTimeline(userId); - }, - dependencies: [localeProvider], -); + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchHomeTimeline(userId); +}, dependencies: [localeProvider]); -final multiUsersTimelineProvider = StreamProvider.family>( - (ref, userIds) { - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchMultiUsersTimeline(userIds); - }, - dependencies: [localeProvider], -); +final multiUsersTimelineProvider = StreamProvider.family>((ref, userIds) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchMultiUsersTimeline(userIds); +}, dependencies: [localeProvider]); final albumTimelineProvider = StreamProvider.autoDispose.family((ref, id) { final album = ref.watch(albumWatcher(id)).value; @@ -65,10 +59,7 @@ final assetSelectionTimelineProvider = StreamProvider((ref) { final assetsTimelineProvider = FutureProvider.family>((ref, assets) { final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - assets, - null, - ); + return timelineService.getTimelineFromAssets(assets, null); }); final lockedTimelineProvider = StreamProvider((ref) { diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index d980ad22b5..742cbd7dea 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -15,26 +15,17 @@ class MultiSelectState { final Set lockedSelectionAssets; final bool forceEnable; - const MultiSelectState({ - required this.selectedAssets, - required this.lockedSelectionAssets, - this.forceEnable = false, - }); + const MultiSelectState({required this.selectedAssets, required this.lockedSelectionAssets, this.forceEnable = false}); bool get isEnabled => selectedAssets.isNotEmpty; /// Cloud only - bool get hasRemote => selectedAssets.any( - (asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged, - ); + bool get hasRemote => + selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); - bool get hasLocal => selectedAssets.any( - (asset) => asset.storage == AssetState.local, - ); + bool get hasLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); - bool get hasMerged => selectedAssets.any( - (asset) => asset.storage == AssetState.merged, - ); + bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); MultiSelectState copyWith({ Set? selectedAssets, @@ -74,12 +65,7 @@ class MultiSelectNotifier extends Notifier { @override MultiSelectState build() { - return _defaultState ?? - const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + return _defaultState ?? const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } void selectAsset(BaseAsset asset) { @@ -87,9 +73,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: {...state.selectedAssets, asset}, - ); + state = state.copyWith(selectedAssets: {...state.selectedAssets, asset}); } void deselectAsset(BaseAsset asset) { @@ -97,9 +81,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: state.selectedAssets.where((a) => a != asset).toSet(), - ); + state = state.copyWith(selectedAssets: state.selectedAssets.where((a) => a != asset).toSet()); } void toggleAssetSelection(BaseAsset asset) { @@ -111,11 +93,7 @@ class MultiSelectNotifier extends Notifier { } void reset() { - state = const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + state = const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } /// Bucket bulk operations @@ -125,9 +103,7 @@ class MultiSelectNotifier extends Notifier { selectedAssets.addAll(assets); - state = state.copyWith( - selectedAssets: selectedAssets, - ); + state = state.copyWith(selectedAssets: selectedAssets); } void deselectBucket(int offset, int bucketCount) async { @@ -164,20 +140,15 @@ class MultiSelectNotifier extends Notifier { } void setLockedSelectionAssets(Set assets) { - state = state.copyWith( - lockedSelectionAssets: assets, - ); + state = state.copyWith(lockedSelectionAssets: assets); } } -final bucketSelectionProvider = Provider.family>( - (ref, bucketAssets) { - final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); +final bucketSelectionProvider = Provider.family>((ref, bucketAssets) { + final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - if (bucketAssets.isEmpty) return false; + if (bucketAssets.isEmpty) return false; - // Check if all assets in the bucket are selected - return bucketAssets.every((asset) => selectedAssets.contains(asset)); - }, - dependencies: [multiSelectProvider, timelineServiceProvider], -); + // Check if all assets in the bucket are selected + return bucketAssets.every((asset) => selectedAssets.contains(asset)); +}, dependencies: [multiSelectProvider, timelineServiceProvider]); diff --git a/mobile/lib/providers/trash.provider.dart b/mobile/lib/providers/trash.provider.dart index c78cccff8a..adf3b1027b 100644 --- a/mobile/lib/providers/trash.provider.dart +++ b/mobile/lib/providers/trash.provider.dart @@ -7,9 +7,7 @@ class TrashNotifier extends StateNotifier { final TrashService _trashService; final _log = Logger('TrashNotifier'); - TrashNotifier( - this._trashService, - ) : super(false); + TrashNotifier(this._trashService) : super(false); Future emptyTrash() async { try { @@ -43,7 +41,5 @@ class TrashNotifier extends StateNotifier { } final trashProvider = StateNotifierProvider((ref) { - return TrashNotifier( - ref.watch(trashServiceProvider), - ); + return TrashNotifier(ref.watch(trashServiceProvider)); }); diff --git a/mobile/lib/providers/upload_profile_image.provider.dart b/mobile/lib/providers/upload_profile_image.provider.dart index 0588b31b68..e9e467346b 100644 --- a/mobile/lib/providers/upload_profile_image.provider.dart +++ b/mobile/lib/providers/upload_profile_image.provider.dart @@ -6,26 +6,15 @@ import 'package:image_picker/image_picker.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; -enum UploadProfileStatus { - idle, - loading, - success, - failure, -} +enum UploadProfileStatus { idle, loading, success, failure } class UploadProfileImageState { // enum final UploadProfileStatus status; final String profileImagePath; - const UploadProfileImageState({ - required this.status, - required this.profileImagePath, - }); + const UploadProfileImageState({required this.status, required this.profileImagePath}); - UploadProfileImageState copyWith({ - UploadProfileStatus? status, - String? profileImagePath, - }) { + UploadProfileImageState copyWith({UploadProfileStatus? status, String? profileImagePath}) { return UploadProfileImageState( status: status ?? this.status, profileImagePath: profileImagePath ?? this.profileImagePath, @@ -68,29 +57,18 @@ class UploadProfileImageState { class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userService) - : super( - const UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - ), - ); + : super(const UploadProfileImageState(profileImagePath: '', status: UploadProfileStatus.idle)); final UserService _userService; Future upload(XFile file) async { state = state.copyWith(status: UploadProfileStatus.loading); - var profileImagePath = await _userService.createProfileImage( - file.name, - await file.readAsBytes(), - ); + var profileImagePath = await _userService.createProfileImage(file.name, await file.readAsBytes()); if (profileImagePath != null) { debugPrint("Successfully upload profile image"); - state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: profileImagePath, - ); + state = state.copyWith(status: UploadProfileStatus.success, profileImagePath: profileImagePath); return true; } diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index 6c24cc0568..fdc21592b5 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -22,23 +22,14 @@ import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; -enum PendingAction { - assetDelete, - assetUploaded, - assetHidden, - assetTrash, -} +enum PendingAction { assetDelete, assetUploaded, assetHidden, assetTrash } class PendingChange { final String id; final PendingAction action; final dynamic value; - const PendingChange( - this.id, - this.action, - this.value, - ); + const PendingChange(this.id, this.action, this.value); @override String toString() => 'PendingChange(id: $id, action: $action, value: $value)'; @@ -59,17 +50,9 @@ class WebsocketState { final bool isConnected; final List pendingChanges; - const WebsocketState({ - this.socket, - required this.isConnected, - required this.pendingChanges, - }); + const WebsocketState({this.socket, required this.isConnected, required this.pendingChanges}); - WebsocketState copyWith({ - Socket? socket, - bool? isConnected, - List? pendingChanges, - }) { + WebsocketState copyWith({Socket? socket, bool? isConnected, List? pendingChanges}) { return WebsocketState( socket: socket ?? this.socket, isConnected: isConnected ?? this.isConnected, @@ -92,14 +75,7 @@ class WebsocketState { } class WebsocketNotifier extends StateNotifier { - WebsocketNotifier(this._ref) - : super( - const WebsocketState( - socket: null, - isConnected: false, - pendingChanges: [], - ), - ); + WebsocketNotifier(this._ref) : super(const WebsocketState(socket: null, isConnected: false, pendingChanges: [])); final _log = Logger('WebsocketNotifier'); final Ref _ref; @@ -147,29 +123,17 @@ class WebsocketNotifier extends StateNotifier { socket.onConnect((_) { debugPrint("Established Websocket Connection"); - state = WebsocketState( - isConnected: true, - socket: socket, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: true, socket: socket, pendingChanges: state.pendingChanges); }); socket.onDisconnect((_) { debugPrint("Disconnect to Websocket Connection"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); socket.on('error', (errorMessage) { _log.severe("Websocket Error - $errorMessage"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); if (!Store.isBetaTimelineEnabled) { @@ -200,11 +164,7 @@ class WebsocketNotifier extends StateNotifier { var socket = state.socket?.disconnect(); if (socket?.disconnected == true) { - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); } } @@ -248,10 +208,7 @@ class WebsocketNotifier extends StateNotifier { void addPendingChange(PendingAction action, dynamic value) { final now = DateTime.now(); state = state.copyWith( - pendingChanges: [ - ...state.pendingChanges, - PendingChange(now.millisecondsSinceEpoch.toString(), action, value), - ], + pendingChanges: [...state.pendingChanges, PendingChange(now.millisecondsSinceEpoch.toString(), action, value)], ); _debounce.run(handlePendingChanges); } @@ -264,9 +221,7 @@ class WebsocketNotifier extends StateNotifier { await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); await _ref.read(assetProvider.notifier).getAllAsset(); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList()); } } @@ -275,9 +230,7 @@ class WebsocketNotifier extends StateNotifier { if (deleteChanges.isNotEmpty) { List remoteIds = deleteChanges.map((a) => a.value.toString()).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList()); } } @@ -304,9 +257,7 @@ class WebsocketNotifier extends StateNotifier { final db = _ref.watch(dbProvider); await db.writeTxn(() => db.assets.deleteAllByRemoteId(remoteIds)); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList()); } } @@ -372,9 +323,7 @@ class WebsocketNotifier extends StateNotifier { } try { - unawaited( - _ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()), - ); + unawaited(_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList())); } catch (error) { _log.severe("Error processing batched AssetUploadReadyV1 events: $error"); } diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 36f380cba7..e8f9abc8c8 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -19,12 +19,7 @@ class ActivityApiRepository extends ApiRepository { return response.map(_toActivity).toList(); } - Future create( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + Future create(String albumId, ActivityType type, {String? assetId, String? comment}) async { final dto = ActivityCreateDto( albumId: albumId, type: type == ActivityType.comment ? ReactionType.comment : ReactionType.like, @@ -45,11 +40,11 @@ class ActivityApiRepository extends ApiRepository { } static Activity _toActivity(ActivityResponseDto dto) => Activity( - id: dto.id, - createdAt: dto.createdAt, - type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, - user: UserConverter.fromSimpleUserDto(dto.user), - assetId: dto.assetId, - comment: dto.comment, - ); + id: dto.id, + createdAt: dto.createdAt, + type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, + user: UserConverter.fromSimpleUserDto(dto.user), + assetId: dto.assetId, + comment: dto.comment, + ); } diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index c65dce325d..2d24004944 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -30,12 +30,7 @@ class AlbumRepository extends DatabaseRepository { Future create(Album album) => txn(() => db.albums.store(album)); - Future getByName( - String name, { - bool? shared, - bool? remote, - bool? owner, - }) { + Future getByName(String name, {bool? shared, bool? remote, bool? owner}) { var query = db.albums.filter().nameEqualTo(name); if (shared != null) { query = query.sharedEqualTo(shared); @@ -58,12 +53,7 @@ class AlbumRepository extends DatabaseRepository { Future delete(int albumId) => txn(() => db.albums.delete(albumId)); - Future> getAll({ - bool? shared, - bool? remote, - int? ownerId, - AlbumSort? sortBy, - }) { + Future> getAll({bool? shared, bool? remote, int? ownerId, AlbumSort? sortBy}) { final baseQuery = db.albums.where(); final QueryBuilder afterWhere; if (remote == null) { @@ -94,9 +84,8 @@ class AlbumRepository extends DatabaseRepository { return db.albums.filter().remoteIdEqualTo(remoteId).findFirst(); } - Future removeUsers(Album album, List users) => txn( - () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), - ); + Future removeUsers(Album album, List users) => + txn(() => album.sharedUsers.update(unlink: users.map(entity.User.fromDto))); Future addAssets(Album album, List assets) => txn(() => album.assets.update(link: assets)); @@ -114,10 +103,7 @@ class AlbumRepository extends DatabaseRepository { Future deleteAllLocal() => txn(() => db.albums.where().localIdIsNotNull().deleteAll()); - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { var query = db.albums.filter().nameContains(searchTerm, caseSensitive: false).remoteIdIsNotNull(); final isarUserId = fastHash(Store.get(StoreKey.currentUser).id); diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index b5b7c72883..11fc1537c5 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final albumApiRepositoryProvider = Provider( - (ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi), -); +final albumApiRepositoryProvider = Provider((ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi)); class AlbumApiRepository extends ApiRepository { final AlbumsApi _api; @@ -34,9 +32,7 @@ class AlbumApiRepository extends ApiRepository { Iterable sharedUserIds = const [], String? description, }) async { - final users = sharedUserIds.map( - (id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor), - ); + final users = sharedUserIds.map((id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor)); final responseDto = await checkNull( _api.createAlbum( CreateAlbumDto( @@ -51,19 +47,9 @@ class AlbumApiRepository extends ApiRepository { } // TODO: Change name after removing old method - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return _toRemoteAlbum(responseDto); @@ -102,16 +88,8 @@ class AlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future<({List added, List duplicates})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List duplicates})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = []; final List duplicates = []; @@ -126,16 +104,8 @@ class AlbumApiRepository extends ApiRepository { return (added: added, duplicates: duplicates); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -149,12 +119,7 @@ class AlbumApiRepository extends ApiRepository { Future addUsers(String albumId, Iterable userIds) async { final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return _toAlbum(response); } diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index 6e9dda173c..89860f4e75 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -18,32 +18,30 @@ class AlbumMediaRepository { DateTimeCond? updateTimeCond, bool? containsPathModified, List? orderBy, - }) => - useCustomFilter - ? FilterOptionGroup( - imageOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - ), - videoOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - durationConstraint: DurationConstraint(allowNullable: true), - ), - containsPathModified: containsPathModified ?? false, - createTimeCond: DateTimeCond.def().copyWith(ignore: true), - updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), - orders: orderBy ?? [], - ) - : null; + }) => useCustomFilter + ? FilterOptionGroup( + imageOption: const FilterOption(needTitle: true, sizeConstraint: SizeConstraint(ignoreSize: true)), + videoOption: const FilterOption( + needTitle: true, + sizeConstraint: SizeConstraint(ignoreSize: true), + durationConstraint: DurationConstraint(allowNullable: true), + ), + containsPathModified: containsPathModified ?? false, + createTimeCond: DateTimeCond.def().copyWith(ignore: true), + updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), + orders: orderBy ?? [], + ) + : null; Future> getAll() async { final filter = useCustomFilter ? CustomFilter.sql(where: '${CustomColumns.base.width} > 0') : FilterOptionGroup(containsPathModified: true); - final List assetPathEntities = - await PhotoManager.getAssetPathList(hasAll: true, filterOption: filter); + final List assetPathEntities = await PhotoManager.getAssetPathList( + hasAll: true, + filterOption: filter, + ); return assetPathEntities.map(_toAlbum).toList(); } @@ -71,10 +69,7 @@ class AlbumMediaRepository { filterOption: _getAlbumFilter( updateTimeCond: modifiedFrom == null && modifiedUntil == null ? null - : DateTimeCond( - min: modifiedFrom ?? DateTime.utc(-271820), - max: modifiedUntil ?? DateTime.utc(275760), - ), + : DateTimeCond(min: modifiedFrom ?? DateTime.utc(-271820), max: modifiedUntil ?? DateTime.utc(275760)), orderBy: orderByModificationDate ? [const OrderOption(type: OrderOptionType.updateDate)] : [], ), ); @@ -84,10 +79,7 @@ class AlbumMediaRepository { } Future get(String id) async { - final assetPathEntity = await AssetPathEntity.fromId( - id, - filterOption: _getAlbumFilter(containsPathModified: true), - ); + final assetPathEntity = await AssetPathEntity.fromId(id, filterOption: _getAlbumFilter(containsPathModified: true)); return _toAlbum(assetPathEntity); } diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 2b35364596..79af8b4921 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -52,16 +52,13 @@ class AssetRepository extends DatabaseRepository { } Future deleteByIds(List ids) => txn(() async { - await db.assets.deleteAll(ids); - await db.exifInfos.deleteAll(ids); - }); + await db.assets.deleteAll(ids); + await db.exifInfos.deleteAll(ids); + }); Future getByRemoteId(String id) => db.assets.getByRemoteId(id); - Future> getAllByRemoteId( - Iterable ids, { - AssetState? state, - }) async { + Future> getAllByRemoteId(Iterable ids, {AssetState? state}) async { if (ids.isEmpty) { return []; } @@ -69,10 +66,7 @@ class AssetRepository extends DatabaseRepository { return _getAllByRemoteIdImpl(ids, state).findAll(); } - QueryBuilder _getAllByRemoteIdImpl( - Iterable ids, - AssetState? state, - ) { + QueryBuilder _getAllByRemoteIdImpl(Iterable ids, AssetState? state) { final query = db.assets.remote(ids).filter(); return switch (state) { null => query.noOp(), @@ -82,12 +76,7 @@ class AssetRepository extends DatabaseRepository { }; } - Future> getAll({ - required String ownerId, - AssetState? state, - AssetSort? sortBy, - int? limit, - }) { + Future> getAll({required String ownerId, AssetState? state, AssetSort? sortBy, int? limit}) { final baseQuery = db.assets.where(); final isarUserIds = fastHash(ownerId); final QueryBuilder filteredQuery = switch (state) { @@ -133,19 +122,15 @@ class AssetRepository extends DatabaseRepository { return asset; } - Future upsertDuplicatedAssets(Iterable duplicatedAssets) => txn( - () => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()), - ); + Future upsertDuplicatedAssets(Iterable duplicatedAssets) => + txn(() => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList())); Future> getAllDuplicatedAssetIds() => db.duplicatedAssets.where().idProperty().findAll(); Future getByOwnerIdChecksum(int ownerId, String checksum) => db.assets.getByOwnerIdChecksum(ownerId, checksum); - Future> getAllByOwnerIdChecksum( - List ownerIds, - List checksums, - ) => + Future> getAllByOwnerIdChecksum(List ownerIds, List checksums) => db.assets.getAllByOwnerIdChecksum(ownerIds, checksums); Future> getAllLocal() => db.assets.where().localIdIsNotNull().findAll(); @@ -211,26 +196,25 @@ Future> _getMatchesImpl( int ownerId, List assets, int limit, -) => - query - .ownerIdEqualTo(ownerId) - .anyOf( - assets, - (q, Asset a) => q - .fileNameEqualTo(a.fileName) - .and() - .durationInSecondsEqualTo(a.durationInSeconds) - .and() - .fileCreatedAtBetween( - a.fileCreatedAt.subtract(const Duration(hours: 12)), - a.fileCreatedAt.add(const Duration(hours: 12)), - ) - .and() - .not() - .checksumEqualTo(a.checksum), - ) - .sortByFileName() - .thenByFileCreatedAt() - .thenByFileModifiedAt() - .limit(limit) - .findAll(); +) => query + .ownerIdEqualTo(ownerId) + .anyOf( + assets, + (q, Asset a) => q + .fileNameEqualTo(a.fileName) + .and() + .durationInSecondsEqualTo(a.durationInSeconds) + .and() + .fileCreatedAtBetween( + a.fileCreatedAt.subtract(const Duration(hours: 12)), + a.fileCreatedAt.add(const Duration(hours: 12)), + ) + .and() + .not() + .checksumEqualTo(a.checksum), + ) + .sortByFileName() + .thenByFileCreatedAt() + .thenByFileModifiedAt() + .limit(limit) + .findAll(); diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index b85ebdea6b..26147292d7 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -23,17 +23,10 @@ class AssetApiRepository extends ApiRepository { final StacksApi _stacksApi; final TrashApi _trashApi; - AssetApiRepository( - this._api, - this._searchApi, - this._stacksApi, - this._trashApi, - ); + AssetApiRepository(this._api, this._searchApi, this._stacksApi, this._trashApi); Future update(String id, {String? description}) async { - final response = await checkNull( - _api.updateAsset(id, UpdateAssetDto(description: description)), - ); + final response = await checkNull(_api.updateAsset(id, UpdateAssetDto(description: description))); return Asset.remote(response); } @@ -44,13 +37,7 @@ class AssetApiRepository extends ApiRepository { int currentPage = 1; while (hasNext) { final response = await checkNull( - _searchApi.searchAssets( - MetadataSearchDto( - personIds: personIds, - page: currentPage, - size: 1000, - ), - ), + _searchApi.searchAssets(MetadataSearchDto(personIds: personIds, page: currentPage, size: 1000)), ); result.addAll(response.assets.items.map(Asset.remote)); hasNext = response.assets.nextPage != null; @@ -67,35 +54,16 @@ class AssetApiRepository extends ApiRepository { await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); } - Future updateVisibility( - List ids, - AssetVisibilityEnum visibility, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)), - ); + Future updateVisibility(List ids, AssetVisibilityEnum visibility) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility))); } - Future updateFavorite( - List ids, - bool isFavorite, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite), - ); + Future updateFavorite(List ids, bool isFavorite) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite)); } - Future updateLocation( - List ids, - LatLng location, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto( - ids: ids, - latitude: location.latitude, - longitude: location.longitude, - ), - ); + Future updateLocation(List ids, LatLng location) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); } Future stack(List ids) async { @@ -113,11 +81,11 @@ class AssetApiRepository extends ApiRepository { } _mapVisibility(AssetVisibilityEnum visibility) => switch (visibility) { - AssetVisibilityEnum.timeline => AssetVisibility.timeline, - AssetVisibilityEnum.hidden => AssetVisibility.hidden, - AssetVisibilityEnum.locked => AssetVisibility.locked, - AssetVisibilityEnum.archive => AssetVisibility.archive, - }; + AssetVisibilityEnum.timeline => AssetVisibility.timeline, + AssetVisibilityEnum.hidden => AssetVisibility.hidden, + AssetVisibilityEnum.locked => AssetVisibility.locked, + AssetVisibilityEnum.archive => AssetVisibility.archive, + }; Future getAssetMIMEType(String assetId) async { final response = await checkNull(_api.getAssetInfo(assetId)); @@ -129,10 +97,6 @@ class AssetApiRepository extends ApiRepository { extension on StackResponseDto { StackResponse toStack() { - return StackResponse( - id: id, - primaryAssetId: primaryAssetId, - assetIds: assets.map((asset) => asset.id).toList(), - ); + return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList()); } } diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 1355890766..a0af217f0c 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -14,9 +14,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:share_plus/share_plus.dart'; -final assetMediaRepositoryProvider = Provider( - (ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)), -); +final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider))); class AssetMediaRepository { final AssetApiRepository _assetApiRepository; @@ -77,8 +75,8 @@ class AssetMediaRepository { final localId = (asset is LocalAsset) ? asset.id : asset is RemoteAsset - ? asset.localId - : null; + ? asset.localId + : null; if (localId != null) { File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile; downloadedXFiles.add(XFile(f!.path)); diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index 992be918f9..e488f69578 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -13,21 +13,12 @@ class AuthApiRepository extends ApiRepository { AuthApiRepository(this._apiService); Future changePassword(String newPassword) async { - await _apiService.usersApi.updateMyUser( - UserUpdateMeDto( - password: newPassword, - ), - ); + await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: newPassword)); } Future login(String email, String password) async { final loginResponseDto = await checkNull( - _apiService.authenticationApi.login( - LoginCredentialDto( - email: email, - password: password, - ), - ), + _apiService.authenticationApi.login(LoginCredentialDto(email: email, password: password)), ); return _mapLoginReponse(loginResponseDto); diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart index b185501d19..8bd5a85542 100644 --- a/mobile/lib/repositories/biometric.repository.dart +++ b/mobile/lib/repositories/biometric.repository.dart @@ -15,15 +15,10 @@ class BiometricRepository { final bool canAuthenticate = canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); final availableBiometric = await _localAuth.getAvailableBiometrics(); - return BiometricStatus( - canAuthenticate: canAuthenticate, - availableBiometrics: availableBiometric, - ); + return BiometricStatus(canAuthenticate: canAuthenticate, availableBiometrics: availableBiometric); } Future authenticate(String? message) async { - return _localAuth.authenticate( - localizedReason: message ?? 'please_auth_to_access'.tr(), - ); + return _localAuth.authenticate(localizedReason: message ?? 'please_auth_to_access'.tr()); } } diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 5908e31786..6f38a802e2 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -64,10 +64,7 @@ class DownloadRepository { } Future> getLiveVideoTasks() { - return _downloader.database.allRecordsWithStatus( - TaskStatus.complete, - group: kDownloadGroupLivePhoto, - ); + return _downloader.database.allRecordsWithStatus(TaskStatus.complete, group: kDownloadGroupLivePhoto); } Future deleteRecordsWithIds(List ids) { diff --git a/mobile/lib/repositories/drift_album_api_repository.dart b/mobile/lib/repositories/drift_album_api_repository.dart index f56cc9fbed..6de025fb47 100644 --- a/mobile/lib/repositories/drift_album_api_repository.dart +++ b/mobile/lib/repositories/drift_album_api_repository.dart @@ -14,34 +14,16 @@ class DriftAlbumApiRepository extends ApiRepository { DriftAlbumApiRepository(this._api); - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return responseDto.toRemoteAlbum(); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -53,16 +35,8 @@ class DriftAlbumApiRepository extends ApiRepository { return (removed: removed, failed: failed); } - Future<({List added, List failed})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List failed})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = [], failed = []; for (final dto in response) { if (dto.success) { @@ -108,17 +82,9 @@ class DriftAlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future addUsers( - String albumId, - Iterable userIds, - ) async { + Future addUsers(String albumId, Iterable userIds) async { final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return response.toRemoteAlbum(); } } diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 86f99e07dd..6d429e4777 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -10,56 +10,23 @@ final fileMediaRepositoryProvider = Provider((ref) => const FileMediaRepository( class FileMediaRepository { const FileMediaRepository(); - Future saveImage( - Uint8List data, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImage( - data, - filename: title, - title: title, - relativePath: relativePath, - ); + Future saveImage(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveImageWithFile( - String filePath, { - String? title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImageWithPath( - filePath, - title: title, - relativePath: relativePath, - ); + Future saveImageWithFile(String filePath, {String? title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImageWithPath(filePath, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveLivePhoto({ - required File image, - required File video, - required String title, - }) async { - final entity = await PhotoManager.editor.darwin.saveLivePhoto( - imageFile: image, - videoFile: video, - title: title, - ); + Future saveLivePhoto({required File image, required File video, required String title}) async { + final entity = await PhotoManager.editor.darwin.saveLivePhoto(imageFile: image, videoFile: video, title: title); return AssetMediaRepository.toAsset(entity); } - Future saveVideo( - File file, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + Future saveVideo(File file, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveVideo(file, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } diff --git a/mobile/lib/repositories/folder_api.repository.dart b/mobile/lib/repositories/folder_api.repository.dart index 9fcb57ae21..dfda19e45e 100644 --- a/mobile/lib/repositories/folder_api.repository.dart +++ b/mobile/lib/repositories/folder_api.repository.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final folderApiRepositoryProvider = Provider( - (ref) => FolderApiRepository( - ref.watch(apiServiceProvider).viewApi, - ), -); +final folderApiRepositoryProvider = Provider((ref) => FolderApiRepository(ref.watch(apiServiceProvider).viewApi)); class FolderApiRepository extends ApiRepository { final ViewApi _api; diff --git a/mobile/lib/repositories/gcast.repository.dart b/mobile/lib/repositories/gcast.repository.dart index f896de2ab0..db3e0f45d0 100644 --- a/mobile/lib/repositories/gcast.repository.dart +++ b/mobile/lib/repositories/gcast.repository.dart @@ -33,19 +33,13 @@ class GCastRepository { }); // open the default receiver - sendMessage(CastSession.kNamespaceReceiver, { - 'type': 'LAUNCH', - 'appId': 'CC1AD845', - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': 'LAUNCH', 'appId': 'CC1AD845'}); } Future disconnect() async { final sessionID = getSessionId(); - sendMessage(CastSession.kNamespaceReceiver, { - 'type': "STOP", - "sessionId": sessionID, - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': "STOP", "sessionId": sessionID}); // wait 500ms to ensure the stop command is processed await Future.delayed(const Duration(milliseconds: 500)); diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index 23b6fadebb..7f5ce62e0c 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final partnerRepositoryProvider = Provider( - (ref) => PartnerRepository(ref.watch(dbProvider)), -); +final partnerRepositoryProvider = Provider((ref) => PartnerRepository(ref.watch(dbProvider))); class PartnerRepository extends DatabaseRepository { const PartnerRepository(super.db); @@ -23,12 +21,14 @@ class PartnerRepository extends DatabaseRepository { } Stream> watchSharedBy() { - return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } Stream> watchSharedWith() { - return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 00de1ea2f1..82554d62e8 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -5,16 +5,9 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -enum Direction { - sharedWithMe, - sharedByMe, -} +enum Direction { sharedWithMe, sharedByMe } -final partnerApiRepositoryProvider = Provider( - (ref) => PartnerApiRepository( - ref.watch(apiServiceProvider).partnersApi, - ), -); +final partnerApiRepositoryProvider = Provider((ref) => PartnerApiRepository(ref.watch(apiServiceProvider).partnersApi)); class PartnerApiRepository extends ApiRepository { final PartnersApi _api; @@ -23,9 +16,7 @@ class PartnerApiRepository extends ApiRepository { Future> getAll(Direction direction) async { final response = await checkNull( - _api.getPartners( - direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_, - ), + _api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_), ); return response.map(UserConverter.fromPartnerDto).toList(); } @@ -38,12 +29,7 @@ class PartnerApiRepository extends ApiRepository { Future delete(String id) => _api.removePartner(id); Future update(String id, {required bool inTimeline}) async { - final dto = await checkNull( - _api.updatePartner( - id, - UpdatePartnerDto(inTimeline: inTimeline), - ), - ); + final dto = await checkNull(_api.updatePartner(id, UpdatePartnerDto(inTimeline: inTimeline))); return UserConverter.fromPartnerDto(dto); } } diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 26f11dd51d..545ad06741 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -4,9 +4,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final personApiRepositoryProvider = Provider( - (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi), -); +final personApiRepositoryProvider = Provider((ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi)); class PersonApiRepository extends ApiRepository { final PeopleApi _api; @@ -19,17 +17,15 @@ class PersonApiRepository extends ApiRepository { } Future update(String id, {String? name}) async { - final dto = await checkNull( - _api.updatePerson(id, PersonUpdateDto(name: name)), - ); + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name))); return _toPerson(dto); } static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( - birthDate: dto.birthDate, - id: dto.id, - isHidden: dto.isHidden, - name: dto.name, - thumbnailPath: dto.thumbnailPath, - ); + birthDate: dto.birthDate, + id: dto.id, + isHidden: dto.isHidden, + name: dto.name, + thumbnailPath: dto.thumbnailPath, + ); } diff --git a/mobile/lib/repositories/sessions_api.repository.dart b/mobile/lib/repositories/sessions_api.repository.dart index 8ef43d54f2..f25e724f19 100644 --- a/mobile/lib/repositories/sessions_api.repository.dart +++ b/mobile/lib/repositories/sessions_api.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; final sessionsAPIRepositoryProvider = Provider( - (ref) => SessionsAPIRepository( - ref.watch(apiServiceProvider).sessionsApi, - ), + (ref) => SessionsAPIRepository(ref.watch(apiServiceProvider).sessionsApi), ); class SessionsAPIRepository extends ApiRepository { @@ -15,19 +13,9 @@ class SessionsAPIRepository extends ApiRepository { SessionsAPIRepository(this._api); - Future createSession( - String deviceType, - String deviceOS, { - int? duration, - }) async { + Future 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: deviceType, deviceOS: deviceOS, duration: duration)), ); return SessionCreateResponse( diff --git a/mobile/lib/repositories/share_handler.repository.dart b/mobile/lib/repositories/share_handler.repository.dart index e739ebe6ae..4650b04a79 100644 --- a/mobile/lib/repositories/share_handler.repository.dart +++ b/mobile/lib/repositories/share_handler.repository.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:share_handler/share_handler.dart'; -final shareHandlerRepositoryProvider = Provider( - (ref) => ShareHandlerRepository(), -); +final shareHandlerRepositoryProvider = Provider((ref) => ShareHandlerRepository()); class ShareHandlerRepository { ShareHandlerRepository(); @@ -28,9 +26,7 @@ class ShareHandlerRepository { }); } - List _buildPayload( - List attachments, - ) { + List _buildPayload(List attachments) { final payload = []; for (final attachment in attachments) { diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index 1579300bc6..c8c173b6f6 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -48,10 +48,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchAlbumTimeline( - Album album, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchAlbumTimeline(Album album, GroupAssetsBy groupAssetByOption) { final query = album.assets.filter().isTrashedEqualTo(false).not().visibilityEqualTo(AssetVisibilityEnum.locked); final withSortedOption = switch (album.sortOrder) { @@ -81,10 +78,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchHomeTimeline( - String userId, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchHomeTimeline(String userId, GroupAssetsBy groupAssetByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) @@ -97,10 +91,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Stream watchMultiUsersTimeline( - List userIds, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchMultiUsersTimeline(List userIds, GroupAssetsBy groupAssetByOption) { final isarUserIds = userIds.map(fastHash).toList(); final query = db.assets .where() @@ -113,10 +104,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy getGroupByOption, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy getGroupByOption) { return RenderList.fromAssets(assets, getGroupByOption); } @@ -134,10 +122,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchLockedTimeline( - String userId, - GroupAssetsBy getGroupByOption, - ) { + Stream watchLockedTimeline(String userId, GroupAssetsBy getGroupByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 1510eb208f..220dbf81c3 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -54,26 +54,11 @@ class UploadRepository { Future getUploadInfo() async { final [enqueuedTasks, runningTasks, canceledTasks, waitingTasks, pausedTasks] = await Future.wait([ - FileDownloader().database.allRecordsWithStatus( - TaskStatus.enqueued, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.running, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.canceled, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.waitingToRetry, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.paused, - group: kBackupGroup, - ), + FileDownloader().database.allRecordsWithStatus(TaskStatus.enqueued, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.running, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.canceled, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.waitingToRetry, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.paused, group: kBackupGroup), ]); debugPrint(""" diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart index 09532f4b78..f21d31e1ec 100644 --- a/mobile/lib/repositories/widget.repository.dart +++ b/mobile/lib/repositories/widget.repository.dart @@ -11,10 +11,7 @@ class WidgetRepository { } Future refresh(String iosName, String androidName) async { - await HomeWidget.updateWidget( - iOSName: iosName, - qualifiedAndroidName: androidName, - ); + await HomeWidget.updateWidget(iOSName: iosName, qualifiedAndroidName: androidName); } Future setAppGroupId(String appGroupId) async { diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 12766cef66..20973fabd1 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -8,18 +8,11 @@ class AppNavigationObserver extends AutoRouterObserver { /// Riverpod Instance final WidgetRef ref; - AppNavigationObserver({ - required this.ref, - }); + AppNavigationObserver({required this.ref}); @override - Future didChangeTabRoute( - TabPageRoute route, - TabPageRoute previousRoute, - ) async { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } @override @@ -41,13 +34,9 @@ class AppNavigationObserver extends AutoRouterObserver { route.settings.name == null && previousRoute?.settings.name == GalleryViewerRoute.name && isInLockedView; if (route.settings.name == LockedRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } @@ -62,13 +51,9 @@ class AppNavigationObserver extends AutoRouterObserver { if (route.settings.name == DriftLockedFolderRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } } diff --git a/mobile/lib/routing/duplicate_guard.dart b/mobile/lib/routing/duplicate_guard.dart index efc649bc41..6f83d5297e 100644 --- a/mobile/lib/routing/duplicate_guard.dart +++ b/mobile/lib/routing/duplicate_guard.dart @@ -8,9 +8,7 @@ class DuplicateGuard extends AutoRouteGuard { void onNavigation(NavigationResolver resolver, StackRouter router) async { // Duplicate navigation if (resolver.route.name == router.current.name) { - debugPrint( - 'DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}', - ); + debugPrint('DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}'); resolver.next(false); } else { resolver.next(true); diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart index d731c7942c..851407ff16 100644 --- a/mobile/lib/routing/locked_guard.dart +++ b/mobile/lib/routing/locked_guard.dart @@ -17,11 +17,7 @@ class LockedGuard extends AutoRouteGuard { final LocalAuthService _localAuth; final _log = Logger("AuthGuard"); - LockedGuard( - this._apiService, - this._secureStorageService, - this._localAuth, - ); + LockedGuard(this._apiService, this._secureStorageService, this._localAuth); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { @@ -58,9 +54,7 @@ class LockedGuard extends AutoRouteGuard { return; } - await _apiService.authenticationApi.unlockAuthSession( - SessionUnlockDto(pinCode: securePinCode), - ); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: securePinCode)); resolver.next(true); } on PlatformException catch (error) { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index a72558508c..f547db65d6 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -150,38 +150,18 @@ class AppRouter extends RootStackRouter { @override late final List routes = [ AutoRoute(page: SplashScreenRoute.page, initial: true), - AutoRoute( - page: PermissionOnboardingRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PermissionOnboardingRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: LoginRoute.page, guards: [_duplicateGuard]), AutoRoute(page: ChangePasswordRoute.page), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), CustomRoute( page: TabControllerRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: PhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: LibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -189,23 +169,10 @@ class AppRouter extends RootStackRouter { page: TabShellRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftSearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftSearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -214,18 +181,9 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _galleryGuard], transitionsBuilder: CustomTransitionsBuilders.zoomedPage, ), - AutoRoute( - page: BackupControllerRoute.page, - guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], - ), - AutoRoute( - page: AllPlacesRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: CreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupControllerRoute.page, guards: [_authGuard, _duplicateGuard, _backupPermissionGuard]), + AutoRoute(page: AllPlacesRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: CreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: EditImageRoute.page), AutoRoute(page: CropImageRoute.page), AutoRoute(page: FilterImageRoute.page), @@ -235,14 +193,8 @@ class AppRouter extends RootStackRouter { transitionsBuilder: TransitionsBuilders.slideLeft, ), AutoRoute(page: AllVideosRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AllMotionPhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AllMotionPhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAssetSelectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -253,23 +205,14 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: AlbumViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumViewerRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAdditionalSharedUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: BackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumPreviewRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumPreviewRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: FailedBackupStatusRoute.page, guards: [_authGuard, _duplicateGuard], @@ -289,26 +232,13 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - CustomRoute( - page: FolderRoute.page, - guards: [_authGuard], - transitionsBuilder: TransitionsBuilders.fadeIn, - ), - AutoRoute( - page: PartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: PersonResultRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + CustomRoute(page: FolderRoute.page, guards: [_authGuard], transitionsBuilder: TransitionsBuilders.fadeIn), + AutoRoute(page: PartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: PersonResultRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: AllPeopleRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AlbumOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: TrashRoute.page, guards: [_authGuard, _duplicateGuard], @@ -319,28 +249,16 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: SharedLinkEditRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: SharedLinkEditRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: ActivitiesRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, durationInMilliseconds: 200, ), - CustomRoute( - page: MapLocationPickerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BackupOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: HeaderSettingsRoute.page, - guards: [_duplicateGuard], - ), + CustomRoute(page: MapLocationPickerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: HeaderSettingsRoute.page, guards: [_duplicateGuard]), CustomRoute( page: PeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -361,54 +279,18 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: NativeVideoViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ShareIntentRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LockedRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: PinAuthRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: FeatInDevRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: NativeVideoViewerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute( page: AssetViewerRoute.page, guards: [_authGuard, _duplicateGuard], @@ -421,83 +303,26 @@ class AppRouter extends RootStackRouter { ), ), ), - AutoRoute( - page: DriftMemoryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftFavoriteRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftTrashRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftArchiveRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLockedFolderRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftVideoRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAssetSelectionTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftRecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLocalAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftCreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUserSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ChangeExperienceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftMemoryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftFavoriteRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftTrashRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftArchiveRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLockedFolderRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: DriftVideoRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAssetSelectionTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftRecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLocalAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftCreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: DriftPartnerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUploadDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BetaSyncSettingsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 6b7e479cff..f67d1e2623 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -13,7 +13,8 @@ part of 'router.dart'; /// generated route for /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { - const ActivitiesRoute({List? children}) : super(ActivitiesRoute.name, initialChildren: children); + const ActivitiesRoute({List? children}) + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -27,19 +28,20 @@ class ActivitiesRoute extends PageRouteInfo { /// generated route for /// [AlbumAdditionalSharedUserSelectionPage] -class AlbumAdditionalSharedUserSelectionRoute extends PageRouteInfo { +class AlbumAdditionalSharedUserSelectionRoute + extends PageRouteInfo { AlbumAdditionalSharedUserSelectionRoute({ Key? key, required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -73,21 +75,22 @@ class AlbumAdditionalSharedUserSelectionRouteArgs { /// generated route for /// [AlbumAssetSelectionPage] -class AlbumAssetSelectionRoute extends PageRouteInfo { +class AlbumAssetSelectionRoute + extends PageRouteInfo { AlbumAssetSelectionRoute({ Key? key, required Set existingAssets, bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -126,7 +129,8 @@ class AlbumAssetSelectionRouteArgs { /// generated route for /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { - const AlbumOptionsRoute({List? children}) : super(AlbumOptionsRoute.name, initialChildren: children); + const AlbumOptionsRoute({List? children}) + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -146,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -177,16 +181,17 @@ class AlbumPreviewRouteArgs { /// generated route for /// [AlbumSharedUserSelectionPage] -class AlbumSharedUserSelectionRoute extends PageRouteInfo { +class AlbumSharedUserSelectionRoute + extends PageRouteInfo { AlbumSharedUserSelectionRoute({ Key? key, required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -220,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -252,7 +257,8 @@ class AlbumViewerRouteArgs { /// generated route for /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { - const AlbumsRoute({List? children}) : super(AlbumsRoute.name, initialChildren: children); + const AlbumsRoute({List? children}) + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -268,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -283,7 +289,8 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// generated route for /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { - const AllPeopleRoute({List? children}) : super(AllPeopleRoute.name, initialChildren: children); + const AllPeopleRoute({List? children}) + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -298,7 +305,8 @@ class AllPeopleRoute extends PageRouteInfo { /// generated route for /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { - const AllPlacesRoute({List? children}) : super(AllPlacesRoute.name, initialChildren: children); + const AllPlacesRoute({List? children}) + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -313,7 +321,8 @@ class AllPlacesRoute extends PageRouteInfo { /// generated route for /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { - const AllVideosRoute({List? children}) : super(AllVideosRoute.name, initialChildren: children); + const AllVideosRoute({List? children}) + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -333,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -365,7 +374,8 @@ class AppLogDetailRouteArgs { /// generated route for /// [AppLogPage] class AppLogRoute extends PageRouteInfo { - const AppLogRoute({List? children}) : super(AppLogRoute.name, initialChildren: children); + const AppLogRoute({List? children}) + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -380,7 +390,8 @@ class AppLogRoute extends PageRouteInfo { /// generated route for /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { - const ArchiveRoute({List? children}) : super(ArchiveRoute.name, initialChildren: children); + const ArchiveRoute({List? children}) + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -402,15 +413,15 @@ class AssetViewerRoute extends PageRouteInfo { int? heroOffset, List? children, }) : super( - AssetViewerRoute.name, - args: AssetViewerRouteArgs( - key: key, - initialIndex: initialIndex, - timelineService: timelineService, - heroOffset: heroOffset, - ), - initialChildren: children, - ); + AssetViewerRoute.name, + args: AssetViewerRouteArgs( + key: key, + initialIndex: initialIndex, + timelineService: timelineService, + heroOffset: heroOffset, + ), + initialChildren: children, + ); static const String name = 'AssetViewerRoute'; @@ -454,7 +465,7 @@ class AssetViewerRouteArgs { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -470,7 +481,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -485,7 +496,8 @@ class BackupControllerRoute extends PageRouteInfo { /// generated route for /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { - const BackupOptionsRoute({List? children}) : super(BackupOptionsRoute.name, initialChildren: children); + const BackupOptionsRoute({List? children}) + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -501,7 +513,7 @@ class BackupOptionsRoute extends PageRouteInfo { /// [BetaSyncSettingsPage] class BetaSyncSettingsRoute extends PageRouteInfo { const BetaSyncSettingsRoute({List? children}) - : super(BetaSyncSettingsRoute.name, initialChildren: children); + : super(BetaSyncSettingsRoute.name, initialChildren: children); static const String name = 'BetaSyncSettingsRoute'; @@ -521,13 +533,13 @@ class ChangeExperienceRoute extends PageRouteInfo { required bool switchingToBeta, List? children, }) : super( - ChangeExperienceRoute.name, - args: ChangeExperienceRouteArgs( - key: key, - switchingToBeta: switchingToBeta, - ), - initialChildren: children, - ); + ChangeExperienceRoute.name, + args: ChangeExperienceRouteArgs( + key: key, + switchingToBeta: switchingToBeta, + ), + initialChildren: children, + ); static const String name = 'ChangeExperienceRoute'; @@ -560,7 +572,7 @@ class ChangeExperienceRouteArgs { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -580,10 +592,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -620,10 +632,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -658,7 +670,8 @@ class CropImageRouteArgs { /// generated route for /// [DriftAlbumsPage] class DriftAlbumsRoute extends PageRouteInfo { - const DriftAlbumsRoute({List? children}) : super(DriftAlbumsRoute.name, initialChildren: children); + const DriftAlbumsRoute({List? children}) + : super(DriftAlbumsRoute.name, initialChildren: children); static const String name = 'DriftAlbumsRoute'; @@ -673,7 +686,8 @@ class DriftAlbumsRoute extends PageRouteInfo { /// generated route for /// [DriftArchivePage] class DriftArchiveRoute extends PageRouteInfo { - const DriftArchiveRoute({List? children}) : super(DriftArchiveRoute.name, initialChildren: children); + const DriftArchiveRoute({List? children}) + : super(DriftArchiveRoute.name, initialChildren: children); static const String name = 'DriftArchiveRoute'; @@ -687,19 +701,20 @@ class DriftArchiveRoute extends PageRouteInfo { /// generated route for /// [DriftAssetSelectionTimelinePage] -class DriftAssetSelectionTimelineRoute extends PageRouteInfo { +class DriftAssetSelectionTimelineRoute + extends PageRouteInfo { DriftAssetSelectionTimelineRoute({ Key? key, Set lockedSelectionAssets = const {}, List? children, }) : super( - DriftAssetSelectionTimelineRoute.name, - args: DriftAssetSelectionTimelineRouteArgs( - key: key, - lockedSelectionAssets: lockedSelectionAssets, - ), - initialChildren: children, - ); + DriftAssetSelectionTimelineRoute.name, + args: DriftAssetSelectionTimelineRouteArgs( + key: key, + lockedSelectionAssets: lockedSelectionAssets, + ), + initialChildren: children, + ); static const String name = 'DriftAssetSelectionTimelineRoute'; @@ -737,7 +752,7 @@ class DriftAssetSelectionTimelineRouteArgs { /// [DriftBackupAlbumSelectionPage] class DriftBackupAlbumSelectionRoute extends PageRouteInfo { const DriftBackupAlbumSelectionRoute({List? children}) - : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); + : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'DriftBackupAlbumSelectionRoute'; @@ -752,7 +767,8 @@ class DriftBackupAlbumSelectionRoute extends PageRouteInfo { /// generated route for /// [DriftBackupPage] class DriftBackupRoute extends PageRouteInfo { - const DriftBackupRoute({List? children}) : super(DriftBackupRoute.name, initialChildren: children); + const DriftBackupRoute({List? children}) + : super(DriftBackupRoute.name, initialChildren: children); static const String name = 'DriftBackupRoute'; @@ -768,7 +784,7 @@ class DriftBackupRoute extends PageRouteInfo { /// [DriftCreateAlbumPage] class DriftCreateAlbumRoute extends PageRouteInfo { const DriftCreateAlbumRoute({List? children}) - : super(DriftCreateAlbumRoute.name, initialChildren: children); + : super(DriftCreateAlbumRoute.name, initialChildren: children); static const String name = 'DriftCreateAlbumRoute'; @@ -783,7 +799,8 @@ class DriftCreateAlbumRoute extends PageRouteInfo { /// generated route for /// [DriftFavoritePage] class DriftFavoriteRoute extends PageRouteInfo { - const DriftFavoriteRoute({List? children}) : super(DriftFavoriteRoute.name, initialChildren: children); + const DriftFavoriteRoute({List? children}) + : super(DriftFavoriteRoute.name, initialChildren: children); static const String name = 'DriftFavoriteRoute'; @@ -798,7 +815,8 @@ class DriftFavoriteRoute extends PageRouteInfo { /// generated route for /// [DriftLibraryPage] class DriftLibraryRoute extends PageRouteInfo { - const DriftLibraryRoute({List? children}) : super(DriftLibraryRoute.name, initialChildren: children); + const DriftLibraryRoute({List? children}) + : super(DriftLibraryRoute.name, initialChildren: children); static const String name = 'DriftLibraryRoute'; @@ -814,7 +832,7 @@ class DriftLibraryRoute extends PageRouteInfo { /// [DriftLocalAlbumsPage] class DriftLocalAlbumsRoute extends PageRouteInfo { const DriftLocalAlbumsRoute({List? children}) - : super(DriftLocalAlbumsRoute.name, initialChildren: children); + : super(DriftLocalAlbumsRoute.name, initialChildren: children); static const String name = 'DriftLocalAlbumsRoute'; @@ -830,7 +848,7 @@ class DriftLocalAlbumsRoute extends PageRouteInfo { /// [DriftLockedFolderPage] class DriftLockedFolderRoute extends PageRouteInfo { const DriftLockedFolderRoute({List? children}) - : super(DriftLockedFolderRoute.name, initialChildren: children); + : super(DriftLockedFolderRoute.name, initialChildren: children); static const String name = 'DriftLockedFolderRoute'; @@ -851,14 +869,14 @@ class DriftMemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - DriftMemoryRoute.name, - args: DriftMemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + DriftMemoryRoute.name, + args: DriftMemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'DriftMemoryRoute'; @@ -896,16 +914,17 @@ class DriftMemoryRouteArgs { /// generated route for /// [DriftPartnerDetailPage] -class DriftPartnerDetailRoute extends PageRouteInfo { +class DriftPartnerDetailRoute + extends PageRouteInfo { DriftPartnerDetailRoute({ Key? key, required PartnerUserDto partner, List? children, }) : super( - DriftPartnerDetailRoute.name, - args: DriftPartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + DriftPartnerDetailRoute.name, + args: DriftPartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'DriftPartnerDetailRoute'; @@ -934,7 +953,8 @@ class DriftPartnerDetailRouteArgs { /// generated route for /// [DriftPartnerPage] class DriftPartnerRoute extends PageRouteInfo { - const DriftPartnerRoute({List? children}) : super(DriftPartnerRoute.name, initialChildren: children); + const DriftPartnerRoute({List? children}) + : super(DriftPartnerRoute.name, initialChildren: children); static const String name = 'DriftPartnerRoute'; @@ -954,10 +974,10 @@ class DriftPlaceDetailRoute extends PageRouteInfo { required String place, List? children, }) : super( - DriftPlaceDetailRoute.name, - args: DriftPlaceDetailRouteArgs(key: key, place: place), - initialChildren: children, - ); + DriftPlaceDetailRoute.name, + args: DriftPlaceDetailRouteArgs(key: key, place: place), + initialChildren: children, + ); static const String name = 'DriftPlaceDetailRoute'; @@ -991,10 +1011,10 @@ class DriftPlaceRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - DriftPlaceRoute.name, - args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), - initialChildren: children, - ); + DriftPlaceRoute.name, + args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), + initialChildren: children, + ); static const String name = 'DriftPlaceRoute'; @@ -1029,7 +1049,7 @@ class DriftPlaceRouteArgs { /// [DriftRecentlyTakenPage] class DriftRecentlyTakenRoute extends PageRouteInfo { const DriftRecentlyTakenRoute({List? children}) - : super(DriftRecentlyTakenRoute.name, initialChildren: children); + : super(DriftRecentlyTakenRoute.name, initialChildren: children); static const String name = 'DriftRecentlyTakenRoute'; @@ -1049,10 +1069,10 @@ class DriftSearchRoute extends PageRouteInfo { SearchFilter? preFilter, List? children, }) : super( - DriftSearchRoute.name, - args: DriftSearchRouteArgs(key: key, preFilter: preFilter), - initialChildren: children, - ); + DriftSearchRoute.name, + args: DriftSearchRouteArgs(key: key, preFilter: preFilter), + initialChildren: children, + ); static const String name = 'DriftSearchRoute'; @@ -1083,7 +1103,8 @@ class DriftSearchRouteArgs { /// generated route for /// [DriftTrashPage] class DriftTrashRoute extends PageRouteInfo { - const DriftTrashRoute({List? children}) : super(DriftTrashRoute.name, initialChildren: children); + const DriftTrashRoute({List? children}) + : super(DriftTrashRoute.name, initialChildren: children); static const String name = 'DriftTrashRoute'; @@ -1099,7 +1120,7 @@ class DriftTrashRoute extends PageRouteInfo { /// [DriftUploadDetailPage] class DriftUploadDetailRoute extends PageRouteInfo { const DriftUploadDetailRoute({List? children}) - : super(DriftUploadDetailRoute.name, initialChildren: children); + : super(DriftUploadDetailRoute.name, initialChildren: children); static const String name = 'DriftUploadDetailRoute'; @@ -1113,16 +1134,17 @@ class DriftUploadDetailRoute extends PageRouteInfo { /// generated route for /// [DriftUserSelectionPage] -class DriftUserSelectionRoute extends PageRouteInfo { +class DriftUserSelectionRoute + extends PageRouteInfo { DriftUserSelectionRoute({ Key? key, required RemoteAlbum album, List? children, }) : super( - DriftUserSelectionRoute.name, - args: DriftUserSelectionRouteArgs(key: key, album: album), - initialChildren: children, - ); + DriftUserSelectionRoute.name, + args: DriftUserSelectionRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'DriftUserSelectionRoute'; @@ -1151,7 +1173,8 @@ class DriftUserSelectionRouteArgs { /// generated route for /// [DriftVideoPage] class DriftVideoRoute extends PageRouteInfo { - const DriftVideoRoute({List? children}) : super(DriftVideoRoute.name, initialChildren: children); + const DriftVideoRoute({List? children}) + : super(DriftVideoRoute.name, initialChildren: children); static const String name = 'DriftVideoRoute'; @@ -1173,15 +1196,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -1225,7 +1248,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -1240,7 +1263,8 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// generated route for /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { - const FavoritesRoute({List? children}) : super(FavoritesRoute.name, initialChildren: children); + const FavoritesRoute({List? children}) + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -1255,7 +1279,8 @@ class FavoritesRoute extends PageRouteInfo { /// generated route for /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { - const FeatInDevRoute({List? children}) : super(FeatInDevRoute.name, initialChildren: children); + const FeatInDevRoute({List? children}) + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -1276,10 +1301,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -1323,10 +1348,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -1365,16 +1390,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -1422,7 +1447,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -1437,7 +1462,8 @@ class HeaderSettingsRoute extends PageRouteInfo { /// generated route for /// [LibraryPage] class LibraryRoute extends PageRouteInfo { - const LibraryRoute({List? children}) : super(LibraryRoute.name, initialChildren: children); + const LibraryRoute({List? children}) + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -1452,7 +1478,8 @@ class LibraryRoute extends PageRouteInfo { /// generated route for /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { - const LocalAlbumsRoute({List? children}) : super(LocalAlbumsRoute.name, initialChildren: children); + const LocalAlbumsRoute({List? children}) + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -1468,7 +1495,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -1488,10 +1515,10 @@ class LocalTimelineRoute extends PageRouteInfo { required LocalAlbum album, List? children, }) : super( - LocalTimelineRoute.name, - args: LocalTimelineRouteArgs(key: key, album: album), - initialChildren: children, - ); + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'LocalTimelineRoute'; @@ -1520,7 +1547,8 @@ class LocalTimelineRouteArgs { /// generated route for /// [LockedPage] class LockedRoute extends PageRouteInfo { - const LockedRoute({List? children}) : super(LockedRoute.name, initialChildren: children); + const LockedRoute({List? children}) + : super(LockedRoute.name, initialChildren: children); static const String name = 'LockedRoute'; @@ -1535,7 +1563,8 @@ class LockedRoute extends PageRouteInfo { /// generated route for /// [LoginPage] class LoginRoute extends PageRouteInfo { - const LoginRoute({List? children}) : super(LoginRoute.name, initialChildren: children); + const LoginRoute({List? children}) + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -1550,7 +1579,8 @@ class LoginRoute extends PageRouteInfo { /// generated route for /// [MainTimelinePage] class MainTimelineRoute extends PageRouteInfo { - const MainTimelineRoute({List? children}) : super(MainTimelineRoute.name, initialChildren: children); + const MainTimelineRoute({List? children}) + : super(MainTimelineRoute.name, initialChildren: children); static const String name = 'MainTimelineRoute'; @@ -1570,13 +1600,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -1614,11 +1644,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -1655,14 +1685,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1709,16 +1739,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1770,10 +1800,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1802,7 +1832,8 @@ class PartnerDetailRouteArgs { /// generated route for /// [PartnerPage] class PartnerRoute extends PageRouteInfo { - const PartnerRoute({List? children}) : super(PartnerRoute.name, initialChildren: children); + const PartnerRoute({List? children}) + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1818,7 +1849,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1834,7 +1865,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1855,14 +1886,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1901,7 +1932,8 @@ class PersonResultRouteArgs { /// generated route for /// [PhotosPage] class PhotosRoute extends PageRouteInfo { - const PhotosRoute({List? children}) : super(PhotosRoute.name, initialChildren: children); + const PhotosRoute({List? children}) + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1921,10 +1953,10 @@ class PinAuthRoute extends PageRouteInfo { bool createPinCode = false, List? children, }) : super( - PinAuthRoute.name, - args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), - initialChildren: children, - ); + PinAuthRoute.name, + args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), + initialChildren: children, + ); static const String name = 'PinAuthRoute'; @@ -1960,13 +1992,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -2000,7 +2032,8 @@ class PlacesCollectionRouteArgs { /// generated route for /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { - const RecentlyTakenRoute({List? children}) : super(RecentlyTakenRoute.name, initialChildren: children); + const RecentlyTakenRoute({List? children}) + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -2020,10 +2053,10 @@ class RemoteAlbumRoute extends PageRouteInfo { required RemoteAlbum album, List? children, }) : super( - RemoteAlbumRoute.name, - args: RemoteAlbumRouteArgs(key: key, album: album), - initialChildren: children, - ); + RemoteAlbumRoute.name, + args: RemoteAlbumRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'RemoteAlbumRoute'; @@ -2053,7 +2086,7 @@ class RemoteAlbumRouteArgs { /// [RemoteMediaSummaryPage] class RemoteMediaSummaryRoute extends PageRouteInfo { const RemoteMediaSummaryRoute({List? children}) - : super(RemoteMediaSummaryRoute.name, initialChildren: children); + : super(RemoteMediaSummaryRoute.name, initialChildren: children); static const String name = 'RemoteMediaSummaryRoute'; @@ -2073,10 +2106,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -2107,7 +2140,8 @@ class SearchRouteArgs { /// generated route for /// [SettingsPage] class SettingsRoute extends PageRouteInfo { - const SettingsRoute({List? children}) : super(SettingsRoute.name, initialChildren: children); + const SettingsRoute({List? children}) + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -2127,10 +2161,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -2164,10 +2198,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -2203,15 +2237,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -2256,7 +2290,8 @@ class SharedLinkEditRouteArgs { /// generated route for /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { - const SharedLinkRoute({List? children}) : super(SharedLinkRoute.name, initialChildren: children); + const SharedLinkRoute({List? children}) + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -2271,7 +2306,8 @@ class SharedLinkRoute extends PageRouteInfo { /// generated route for /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { - const SplashScreenRoute({List? children}) : super(SplashScreenRoute.name, initialChildren: children); + const SplashScreenRoute({List? children}) + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -2286,7 +2322,8 @@ class SplashScreenRoute extends PageRouteInfo { /// generated route for /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { - const TabControllerRoute({List? children}) : super(TabControllerRoute.name, initialChildren: children); + const TabControllerRoute({List? children}) + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -2301,7 +2338,8 @@ class TabControllerRoute extends PageRouteInfo { /// generated route for /// [TabShellPage] class TabShellRoute extends PageRouteInfo { - const TabShellRoute({List? children}) : super(TabShellRoute.name, initialChildren: children); + const TabShellRoute({List? children}) + : super(TabShellRoute.name, initialChildren: children); static const String name = 'TabShellRoute'; @@ -2316,7 +2354,8 @@ class TabShellRoute extends PageRouteInfo { /// generated route for /// [TrashPage] class TrashRoute extends PageRouteInfo { - const TrashRoute({List? children}) : super(TrashRoute.name, initialChildren: children); + const TrashRoute({List? children}) + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 4ed80d9f90..5a23f16534 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -49,11 +49,7 @@ class ActionService { ); Future shareLink(List remoteIds, BuildContext context) async { - context.pushRoute( - SharedLinkEditRoute( - assetsList: remoteIds, - ), - ); + context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); } Future favorite(List remoteIds) async { @@ -67,39 +63,18 @@ class ActionService { } Future archive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.archive, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.archive, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.archive); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.archive); } Future unArchive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } - Future moveToLockFolder( - List remoteIds, - List localIds, - ) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.locked, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.locked, - ); + Future moveToLockFolder(List remoteIds, List localIds) async { + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.locked); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.locked); // Ask user if they want to delete local copies if (localIds.isNotEmpty) { @@ -112,14 +87,8 @@ class ActionService { } Future removeFromLockFolder(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } Future trash(List remoteIds) async { @@ -145,10 +114,7 @@ class ActionService { } } - Future deleteRemoteAndLocal( - List remoteIds, - List localIds, - ) async { + Future deleteRemoteAndLocal(List remoteIds, List localIds) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); @@ -171,10 +137,7 @@ class ActionService { return 0; } - Future editLocation( - List remoteIds, - BuildContext context, - ) async { + Future editLocation(List remoteIds, BuildContext context) async { maplibre.LatLng? initialLatLng; if (remoteIds.length == 1) { final exif = await _remoteAssetRepository.getExif(remoteIds[0]); @@ -184,23 +147,14 @@ class ActionService { } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return false; } - await _assetApiRepository.updateLocation( - remoteIds, - location, - ); - await _remoteAssetRepository.updateLocation( - remoteIds, - location, - ); + await _assetApiRepository.updateLocation(remoteIds, location); + await _remoteAssetRepository.updateLocation(remoteIds, location); return true; } diff --git a/mobile/lib/services/activity.service.dart b/mobile/lib/services/activity.service.dart index 611d985afe..7a0a092e7d 100644 --- a/mobile/lib/services/activity.service.dart +++ b/mobile/lib/services/activity.service.dart @@ -11,10 +11,7 @@ class ActivityService with ErrorLoggerMixin { ActivityService(this._activityApiRepository); - Future> getAllActivities( - String albumId, { - String? assetId, - }) async { + Future> getAllActivities(String albumId, {String? assetId}) async { return logError( () => _activityApiRepository.getAll(albumId, assetId: assetId), defaultValue: [], @@ -41,19 +38,9 @@ class ActivityService with ErrorLoggerMixin { ); } - AsyncFuture addActivity( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + AsyncFuture addActivity(String albumId, ActivityType type, {String? assetId, String? comment}) async { return guardError( - () => _activityApiRepository.create( - albumId, - type, - assetId: assetId, - comment: comment, - ), + () => _activityApiRepository.create(albumId, type, assetId: assetId, comment: comment), errorMessage: "Failed to create $type for album $albumId", ); } diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index 308b9acc44..454f652035 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -77,7 +77,7 @@ class AlbumService { final (selectedIds, excludedIds, onDevice) = await ( _backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()), _backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()), - _albumMediaRepository.getAll() + _albumMediaRepository.getAll(), ).wait; _log.info("Found ${onDevice.length} device albums"); if (selectedIds.isEmpty) { @@ -102,9 +102,7 @@ class AlbumService { } // remove all excluded albums onDevice.removeWhere((e) => excludedIds.contains(e.localId)); - _log.info( - "Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums", - ); + _log.info("Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums"); } final allAlbum = onDevice.firstWhereOrNull((album) => album.isAll); @@ -130,16 +128,11 @@ class AlbumService { return changes; } - Future> _loadExcludedAssetIds( - List albums, - Set excludedAlbumIds, - ) async { + Future> _loadExcludedAssetIds(List albums, Set excludedAlbumIds) async { final Set result = HashSet(); for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) { await batchAlbums - .map( - (album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds)), - ) + .map((album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds))) .wait; } return result; @@ -167,13 +160,10 @@ class AlbumService { _albumApiRepository.getAll(shared: true), // Passing null (or nothing) for `shared` returns only albums that // explicitly belong to us - _albumApiRepository.getAll(shared: null) + _albumApiRepository.getAll(shared: null), ).wait; - final albums = HashSet( - equals: (a, b) => a.remoteId == b.remoteId, - hashCode: (a) => a.remoteId.hashCode, - ); + final albums = HashSet(equals: (a, b) => a.remoteId == b.remoteId, hashCode: (a) => a.remoteId.hashCode); albums.addAll(sharedAlbum); albums.addAll(ownedAlbum); @@ -205,7 +195,7 @@ class AlbumService { */ Future _getNextAlbumName() async { const baseName = "Untitled"; - for (int round = 0;; round++) { + for (int round = 0; ; round++) { final proposedName = "$baseName${round == 0 ? "" : " ($round)"}"; if (null == await _albumRepository.getByName(proposedName, owner: true)) { @@ -214,46 +204,28 @@ class AlbumService { } } - Future createAlbumWithGeneratedName( - Iterable assets, - ) async { - return createAlbum( - await _getNextAlbumName(), - assets, - [], - ); + Future createAlbumWithGeneratedName(Iterable assets) async { + return createAlbum(await _getNextAlbumName(), assets, []); } - Future addAssets( - Album album, - Iterable assets, - ) async { + Future addAssets(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.addAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.addAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); - final List addedAssets = - result.added.map((id) => assets.firstWhere((asset) => asset.remoteId == id)).toList(); + final List addedAssets = result.added + .map((id) => assets.firstWhere((asset) => asset.remoteId == id)) + .toList(); await _updateAssets(album.id, add: addedAssets); - return AlbumAddAssetsResponse( - alreadyInAlbum: result.duplicates, - successfullyAdded: addedAssets.length, - ); + return AlbumAddAssetsResponse(alreadyInAlbum: result.duplicates, successfullyAdded: addedAssets.length); } catch (e) { debugPrint("Error addAssets ${e.toString()}"); } return null; } - Future _updateAssets( - int albumId, { - List add = const [], - List remove = const [], - }) => + Future _updateAssets(int albumId, {List add = const [], List remove = const []}) => _albumRepository.transaction(() async { final album = await _albumRepository.get(albumId); if (album == null) return; @@ -265,10 +237,7 @@ class AlbumService { Future setActivityStatus(Album album, bool enabled) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - activityEnabled: enabled, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, activityEnabled: enabled); album.activityEnabled = updatedAlbum.activityEnabled; await _albumRepository.update(album); return true; @@ -291,9 +260,7 @@ class AlbumService { final List albums = await _albumRepository.getAll(shared: true); final List existing = []; for (Album album in albums) { - existing.addAll( - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]), - ); + existing.addAll(await _assetRepository.getByAlbum(album, notOwnedBy: [userId])); } final List idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing); if (idsToRemove.isNotEmpty) { @@ -319,15 +286,9 @@ class AlbumService { } } - Future removeAsset( - Album album, - Iterable assets, - ) async { + Future removeAsset(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.removeAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.removeAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id)); await _updateAssets(album.id, remove: toRemove.toList()); return true; @@ -337,15 +298,9 @@ class AlbumService { return false; } - Future removeUser( - Album album, - UserDto user, - ) async { + Future removeUser(Album album, UserDto user) async { try { - await _albumApiRepository.removeUser( - album.remoteId!, - userId: user.id, - ); + await _albumApiRepository.removeUser(album.remoteId!, userId: user.id); album.sharedUsers.remove(entity.User.fromDto(user)); await _albumRepository.removeUsers(album, [user]); @@ -360,20 +315,14 @@ class AlbumService { } } - Future addUsers( - Album album, - List userIds, - ) async { + Future addUsers(Album album, List userIds) async { try { final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds); album.sharedUsers.addAll(updatedAlbum.remoteUsers); album.shared = true; - await _albumRepository.addUsers( - album, - album.sharedUsers.map((u) => u.toDto()).toList(), - ); + await _albumRepository.addUsers(album, album.sharedUsers.map((u) => u.toDto()).toList()); await _albumRepository.update(album); return true; @@ -383,15 +332,9 @@ class AlbumService { return false; } - Future changeTitleAlbum( - Album album, - String newAlbumTitle, - ) async { + Future changeTitleAlbum(Album album, String newAlbumTitle) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - name: newAlbumTitle, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, name: newAlbumTitle); album.name = updatedAlbum.name; await _albumRepository.update(album); @@ -402,15 +345,9 @@ class AlbumService { } } - Future changeDescriptionAlbum( - Album album, - String newAlbumDescription, - ) async { + Future changeDescriptionAlbum(Album album, String newAlbumDescription) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - description: newAlbumDescription, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, description: newAlbumDescription); album.description = updatedAlbum.description; await _albumRepository.update(album); @@ -421,26 +358,13 @@ class AlbumService { } } - Future getAlbumByName( - String name, { - bool? remote, - bool? shared, - bool? owner, - }) => - _albumRepository.getByName( - name, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String name, {bool? remote, bool? shared, bool? owner}) => + _albumRepository.getByName(name, remote: remote, shared: shared, owner: owner); /// /// Add the uploaded asset to the selected albums /// - Future syncUploadAlbums( - List albumNames, - List assetIds, - ) async { + Future syncUploadAlbums(List albumNames, List assetIds) async { for (final albumName in albumNames) { Album? album = await getAlbumByName(albumName, remote: true, owner: true); album ??= await createAlbum(albumName, []); @@ -479,10 +403,7 @@ class AlbumService { return _albumRepository.watchAlbum(id); } - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { return _albumRepository.search(searchTerm, filterMode); } diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index b2e73c1b28..fca9080c86 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -127,11 +127,7 @@ class ApiService implements Authentication { } on SocketException catch (_) { return false; } catch (error, stackTrace) { - _log.severe( - "Error while checking server availability", - error, - stackTrace, - ); + _log.severe("Error while checking server availability", error, stackTrace); return false; } return true; @@ -145,10 +141,7 @@ class ApiService implements Authentication { headers.addAll(getRequestHeaders()); final res = await client - .get( - Uri.parse("$baseUrl/.well-known/immich"), - headers: headers, - ) + .get(Uri.parse("$baseUrl/.well-known/immich"), headers: headers) .timeout(const Duration(seconds: 5)); if (res.statusCode == 200) { @@ -211,10 +204,7 @@ class ApiService implements Authentication { } @override - Future applyToParams( - List queryParams, - Map headerParams, - ) { + Future applyToParams(List queryParams, Map headerParams) { return Future(() { var headers = ApiService.getRequestHeaders(); headerParams.addAll(headers); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index cefc52385a..e705912aa5 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -5,26 +5,10 @@ import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { loadPreview(StoreKey.loadPreview, "loadPreview", true), loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), - themeMode( - StoreKey.themeMode, - "themeMode", - "system", - ), // "light","dark","system" - primaryColor( - StoreKey.primaryColor, - "primaryColor", - defaultColorPresetName, - ), - dynamicTheme( - StoreKey.dynamicTheme, - "dynamicTheme", - false, - ), - colorfulInterface( - StoreKey.colorfulInterface, - "colorfulInterface", - true, - ), + themeMode(StoreKey.themeMode, "themeMode", "system"), // "light","dark","system" + primaryColor(StoreKey.primaryColor, "primaryColor", defaultColorPresetName), + dynamicTheme(StoreKey.dynamicTheme, "dynamicTheme", false), + colorfulInterface(StoreKey.colorfulInterface, "colorfulInterface", true), tilesPerRow(StoreKey.tilesPerRow, "tilesPerRow", 4), dynamicLayout(StoreKey.dynamicLayout, "dynamicLayout", false), groupAssetsBy(StoreKey.groupAssetsBy, "groupBy", 0), @@ -33,43 +17,23 @@ enum AppSettingsEnum { "uploadErrorNotificationGracePeriod", 2, ), - backgroundBackupTotalProgress( - StoreKey.backgroundBackupTotalProgress, - "backgroundBackupTotalProgress", - true, - ), + backgroundBackupTotalProgress(StoreKey.backgroundBackupTotalProgress, "backgroundBackupTotalProgress", true), backgroundBackupSingleProgress( StoreKey.backgroundBackupSingleProgress, "backgroundBackupSingleProgress", false, ), storageIndicator(StoreKey.storageIndicator, "storageIndicator", true), - thumbnailCacheSize( - StoreKey.thumbnailCacheSize, - "thumbnailCacheSize", - 10000, - ), + thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), - albumThumbnailCacheSize( - StoreKey.albumThumbnailCacheSize, - "albumThumbnailCacheSize", - 200, - ), - selectedAlbumSortOrder( - StoreKey.selectedAlbumSortOrder, - "selectedAlbumSortOrder", - 0, - ), + albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), + selectedAlbumSortOrder(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 0), advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), logLevel(StoreKey.logLevel, null, 5), // Level.INFO = 5 preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), - loadOriginalVideo( - StoreKey.loadOriginalVideo, - "loadOriginalVideo", - false, - ), + loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), mapThemeMode(StoreKey.mapThemeMode, null, 0), mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), @@ -77,22 +41,13 @@ enum AppSettingsEnum { mapRelativeDate(StoreKey.mapRelativeDate, null, 0), allowSelfSignedSSLCert(StoreKey.selfSignedCert, null, false), ignoreIcloudAssets(StoreKey.ignoreIcloudAssets, null, false), - selectedAlbumSortReverse( - StoreKey.selectedAlbumSortReverse, - null, - false, - ), + selectedAlbumSortReverse(StoreKey.selectedAlbumSortReverse, null, false), enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), syncAlbums(StoreKey.syncAlbums, null, false), autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), - photoManagerCustomFilter( - StoreKey.photoManagerCustomFilter, - null, - true, - ), + photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, false), - enableBackup(StoreKey.enableBackup, null, false), - ; + enableBackup(StoreKey.enableBackup, null, false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index e9318dd0bf..ee61929c81 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -78,8 +78,9 @@ class AssetService { /// required. Returns `true` if there were any changes. Future refreshRemoteAssets() async { final syncedUserIds = await _etagRepository.getAllIds(); - final List syncedUsers = - syncedUserIds.isEmpty ? [] : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); + final List syncedUsers = syncedUserIds.isEmpty + ? [] + : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, @@ -95,10 +96,7 @@ class AssetService { List users, DateTime since, ) async { - final dto = AssetDeltaSyncDto( - updatedAfter: since, - userIds: users.map((e) => e.id).toList(), - ); + final dto = AssetDeltaSyncDto(updatedAfter: since, userIds: users.map((e) => e.id).toList()); final changes = await _apiService.syncApi.getDeltaSync(dto); return changes == null || changes.needsFullSync ? (null, null) @@ -107,19 +105,13 @@ class AssetService { /// Returns the list of people of the given asset id. // If the server is not reachable `null` is returned. - Future?> getRemotePeopleOfAsset( - String remoteId, - ) async { + Future?> getRemotePeopleOfAsset(String remoteId) async { try { final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId); return dto?.people; } catch (error, stack) { - log.severe( - 'Error while getting remote asset info: ${error.toString()}', - error, - stack, - ); + log.severe('Error while getting remote asset info: ${error.toString()}', error, stack); return null; } @@ -133,18 +125,11 @@ class AssetService { String? lastId; // will break on error or once all assets are loaded while (true) { - final dto = AssetFullSyncDto( - limit: chunkSize, - updatedUntil: until, - lastId: lastId, - userId: user.id, - ); + final dto = AssetFullSyncDto(limit: chunkSize, updatedUntil: until, lastId: lastId, userId: user.id); log.fine("Requesting $chunkSize assets from $lastId"); final List? assets = await _apiService.syncApi.getFullSyncForUser(dto); if (assets == null) return null; - log.fine( - "Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}", - ); + log.fine("Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}"); allAssets.addAll(assets.map(Asset.remote)); if (assets.length != chunkSize) break; lastId = assets.last.id; @@ -182,10 +167,7 @@ class AssetService { return a; } - Future updateAssets( - List assets, - UpdateAssetDto updateAssetDto, - ) async { + Future updateAssets(List assets, UpdateAssetDto updateAssetDto) async { return await _apiService.assetsApi.updateAssets( AssetBulkUpdateDto( ids: assets.map((e) => e.remoteId!).toList(), @@ -198,10 +180,7 @@ class AssetService { ); } - Future> changeFavoriteStatus( - List assets, - bool isFavorite, - ) async { + Future> changeFavoriteStatus(List assets, bool isFavorite) async { try { await updateAssets(assets, UpdateAssetDto(isFavorite: isFavorite)); @@ -218,16 +197,11 @@ class AssetService { } } - Future> changeArchiveStatus( - List assets, - bool isArchived, - ) async { + Future> changeArchiveStatus(List assets, bool isArchived) async { try { await updateAssets( assets, - UpdateAssetDto( - visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline, - ), + UpdateAssetDto(visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline), ); for (var element in assets) { @@ -244,15 +218,9 @@ class AssetService { } } - Future?> changeDateTime( - List assets, - String updatedDt, - ) async { + Future?> changeDateTime(List assets, String updatedDt) async { try { - await updateAssets( - assets, - UpdateAssetDto(dateTimeOriginal: updatedDt), - ); + await updateAssets(assets, UpdateAssetDto(dateTimeOriginal: updatedDt)); for (var element in assets) { element.fileCreatedAt = DateTime.parse(updatedDt); @@ -268,24 +236,12 @@ class AssetService { } } - Future?> changeLocation( - List assets, - LatLng location, - ) async { + Future?> changeLocation(List assets, LatLng location) async { try { - await updateAssets( - assets, - UpdateAssetDto( - latitude: location.latitude, - longitude: location.longitude, - ), - ); + await updateAssets(assets, UpdateAssetDto(latitude: location.latitude, longitude: location.longitude)); for (var element in assets) { - element.exifInfo = element.exifInfo?.copyWith( - latitude: location.latitude, - longitude: location.longitude, - ); + element.exifInfo = element.exifInfo?.copyWith(latitude: location.latitude, longitude: location.longitude); } await _syncService.upsertAssetsWithExif(assets); @@ -310,18 +266,13 @@ class AssetService { await refreshRemoteAssets(); final owner = _userService.getMyUser(); - final remoteAssets = await _assetRepository.getAll( - ownerId: owner.id, - state: AssetState.merged, - ); + final remoteAssets = await _assetRepository.getAll(ownerId: owner.id, state: AssetState.merged); /// Map Map> assetToAlbums = {}; for (BackupCandidate candidate in candidates) { - final asset = remoteAssets.firstWhereOrNull( - (a) => a.localId == candidate.asset.localId, - ); + final asset = remoteAssets.firstWhereOrNull((a) => a.localId == candidate.asset.localId); if (asset != null) { for (final albumName in candidate.albumNames) { @@ -342,10 +293,7 @@ class AssetService { } } - Future setDescription( - Asset asset, - String newDescription, - ) async { + Future setDescription(Asset asset, String newDescription) async { final remoteAssetId = asset.remoteId; final localExifId = asset.exifInfo?.assetId; @@ -354,10 +302,7 @@ class AssetService { return; } - final result = await _assetApiRepository.update( - remoteAssetId, - description: newDescription, - ); + final result = await _assetApiRepository.update(remoteAssetId, description: newDescription); final description = result.exifInfo?.description; @@ -437,10 +382,7 @@ class AssetService { } /// Delete assets from the server and unreference from the database - Future deleteRemoteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final candidates = assets.where((a) => a.isRemote); if (candidates.isEmpty) { @@ -448,10 +390,7 @@ class AssetService { } await _apiService.assetsApi.deleteAssets( - AssetBulkDeleteDto( - ids: candidates.map((a) => a.remoteId!).toList(), - force: shouldDeletePermanently, - ), + AssetBulkDeleteDto(ids: candidates.map((a) => a.remoteId!).toList(), force: shouldDeletePermanently), ); /// Update asset info bassed on the deletion type. @@ -470,8 +409,10 @@ class AssetService { await _assetRepository.updateAll(payload.toList()); if (shouldDeletePermanently) { - final remoteAssetIds = - assets.where((asset) => asset.storage == AssetState.remote).map((asset) => asset.id).toList(); + final remoteAssetIds = assets + .where((asset) => asset.storage == AssetState.remote) + .map((asset) => asset.id) + .toList(); await _assetRepository.deleteByIds(remoteAssetIds); } }); @@ -479,10 +420,7 @@ class AssetService { /// Delete assets on both local file system and the server. /// Unreference from the database. - Future deleteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final hasLocal = assets.any((asset) => asset.isLocal); final hasRemote = assets.any((asset) => asset.isRemote); @@ -491,10 +429,7 @@ class AssetService { } if (hasRemote) { - await deleteRemoteAssets( - assets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await deleteRemoteAssets(assets, shouldDeletePermanently: shouldDeletePermanently); } } @@ -512,14 +447,8 @@ class AssetService { return _assetRepository.getMotionAssets(me.id); } - Future setVisibility( - List assets, - AssetVisibilityEnum visibility, - ) async { - await _assetApiRepository.updateVisibility( - assets.map((asset) => asset.remoteId!).toList(), - visibility, - ); + Future setVisibility(List assets, AssetVisibilityEnum visibility) async { + await _assetApiRepository.updateVisibility(assets.map((asset) => asset.remoteId!).toList(), visibility); final updatedAssets = assets.map((asset) { asset.visibility = visibility; diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index e8c4c5e97e..91c23cac1c 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -111,10 +111,7 @@ class AuthService { _log.severe("Error clearing local data", error, stackTrace); }); - await _appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ); + await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false); } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 5302df49ce..dea20b6d98 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -55,8 +55,10 @@ class BackgroundService { String _lastPrintedDetailContent = ""; String? _lastPrintedDetailTitle; late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); bool get isBackgroundInitialized { return _isBackgroundInitialized; @@ -87,15 +89,12 @@ class BackgroundService { int triggerMaxDelay = 50000, }) async { try { - final bool ok = await _foregroundChannel.invokeMethod( - 'configure', - [ - requireUnmetered, - requireCharging, - triggerUpdateDelay, - triggerMaxDelay, - ], - ); + final bool ok = await _foregroundChannel.invokeMethod('configure', [ + requireUnmetered, + requireCharging, + triggerUpdateDelay, + triggerMaxDelay, + ]); return ok; } catch (error) { return false; @@ -140,10 +139,7 @@ class BackgroundService { } Future?> digestFiles(List paths) { - return _foregroundChannel.invokeListMethod( - "digestFiles", - paths, - ); + return _foregroundChannel.invokeListMethod("digestFiles", paths); } /// Updates the notification shown by the background service @@ -158,10 +154,15 @@ class BackgroundService { }) async { try { if (_isBackgroundInitialized) { - return _backgroundChannel.invokeMethod( - 'updateNotification', - [title, content, progress, max, indeterminate, isDetail, onlyIfFG], - ); + return _backgroundChannel.invokeMethod('updateNotification', [ + title, + content, + progress, + max, + indeterminate, + isDetail, + onlyIfFG, + ]); } } catch (error) { debugPrint("[_updateNotification] failed to communicate with plugin"); @@ -170,11 +171,7 @@ class BackgroundService { } /// Shows a new priority notification - Future _showErrorNotification({ - required String title, - String? content, - String? individualTag, - }) async { + Future _showErrorNotification({required String title, String? content, String? individualTag}) async { try { if (_isBackgroundInitialized && _errorGracePeriodExceeded) { return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]); @@ -191,9 +188,7 @@ class BackgroundService { return await _backgroundChannel.invokeMethod('clearErrorNotifications'); } } catch (error) { - debugPrint( - "[_clearErrorNotifications] failed to communicate with plugin", - ); + debugPrint("[_clearErrorNotifications] failed to communicate with plugin"); } return false; } @@ -302,10 +297,7 @@ class BackgroundService { // indefinitely and can run later // Android is fine to wait here until the lock releases final waitForLock = Platform.isIOS - ? acquireLock().timeout( - const Duration(seconds: 5), - onTimeout: () => false, - ) + ? acquireLock().timeout(const Duration(seconds: 5), onTimeout: () => false) : acquireLock(); final bool hasAccess = await waitForLock; @@ -341,20 +333,13 @@ class BackgroundService { final db = await Bootstrap.initIsar(); await Bootstrap.initDomain(db); - final ref = ProviderContainer( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], - ); + final ref = ProviderContainer(overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)]); HttpSSLOptions.apply(); ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); if (kDebugMode) { - debugPrint( - "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}", - ); + debugPrint("[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); } final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select); @@ -418,10 +403,7 @@ class BackgroundService { return false; } - Set toUpload = await backupService.buildUploadCandidates( - selectedAlbums, - excludedAlbums, - ); + Set toUpload = await backupService.buildUploadCandidates(selectedAlbums, excludedAlbums); try { toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); @@ -444,12 +426,7 @@ class BackgroundService { _uploadedAssetsCount = 0; _updateNotification( title: "backup_background_service_in_progress_notification".tr(), - content: notifyTotalProgress - ? formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ) - : null, + content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, progress: 0, max: notifyTotalProgress ? _assetsToUploadCount : 0, indeterminate: !notifyTotalProgress, @@ -463,9 +440,7 @@ class BackgroundService { toUpload, _cancellationToken!, pmProgressHandler: pmProgressHandler, - onSuccess: (result) => _onAssetUploaded( - shouldNotify: notifyTotalProgress, - ), + onSuccess: (result) => _onAssetUploaded(shouldNotify: notifyTotalProgress), onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), onError: _onBackupError, @@ -482,9 +457,7 @@ class BackgroundService { return ok; } - void _onAssetUploaded({ - bool shouldNotify = false, - }) async { + void _onAssetUploaded({bool shouldNotify = false}) async { if (!shouldNotify) { return; } @@ -522,31 +495,27 @@ class BackgroundService { progress: _uploadedAssetsCount, max: _assetsToUploadCount, title: title, - content: formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ), + content: formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount), ); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { _showErrorNotification( - title: - "backup_background_service_upload_failure_notification".tr(namedArgs: {'filename': errorAssetInfo.fileName}), + title: "backup_background_service_upload_failure_notification".tr( + namedArgs: {'filename': errorAssetInfo.fileName}, + ), individualTag: errorAssetInfo.id, ); } - void _onSetCurrentBackupAsset( - CurrentUploadAsset currentUploadAsset, { - bool shouldNotify = false, - }) { + void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset, {bool shouldNotify = false}) { if (!shouldNotify) { return; } - _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index ea332dd1f9..3e29222b4c 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -74,9 +74,8 @@ class BackupService { } } - Future _saveDuplicatedAssetIds(List deviceAssetIds) => _assetRepository.transaction( - () => _assetRepository.upsertDuplicatedAssets(deviceAssetIds), - ); + Future _saveDuplicatedAssetIds(List deviceAssetIds) => + _assetRepository.transaction(() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds)); /// Get duplicated asset id from database Future> getDuplicatedAssetIds() async { @@ -135,8 +134,8 @@ class BackupService { backupAlbum.id, modifiedFrom: useTimeFilter ? - // subtract 2 seconds to prevent missing assets due to rounding issues - backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) + // subtract 2 seconds to prevent missing assets due to rounding issues + backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) : null, modifiedUntil: useTimeFilter ? now : null, ); @@ -149,9 +148,7 @@ class BackupService { for (final asset in assets) { List albumNames = [localAlbum.name]; - final existingAsset = candidates.firstWhereOrNull( - (candidate) => candidate.asset.localId == asset.localId, - ); + final existingAsset = candidates.firstWhereOrNull((candidate) => candidate.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); @@ -168,17 +165,13 @@ class BackupService { } /// Returns a new list of assets not yet uploaded - Future> removeAlreadyUploadedAssets( - Set candidates, - ) async { + Future> removeAlreadyUploadedAssets(Set candidates) async { if (candidates.isEmpty) { return candidates; } final Set duplicatedAssetIds = await getDuplicatedAssetIds(); - candidates.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + candidates.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (candidates.isEmpty) { return candidates; @@ -188,10 +181,7 @@ class BackupService { try { final String deviceId = Store.get(StoreKey.deviceId); final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets( - CheckExistingAssetsDto( - deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), - deviceId: deviceId, - ), + CheckExistingAssetsDto(deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), deviceId: deviceId), ); if (duplicates != null) { existing.addAll(duplicates.existingIds); @@ -215,8 +205,10 @@ class BackupService { if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) { // double check that permission is granted here, to guard against // uploading corrupt assets without EXIF information - _log.warning("Media location permission is not granted. " - "Cannot access original assets for backup."); + _log.warning( + "Media location permission is not granted. " + "Cannot access original assets for backup.", + ); return false; } @@ -232,13 +224,11 @@ class BackupService { /// Upload images before video assets for background tasks /// these are further sorted by using their creation date List _sortPhotosFirst(List candidates) { - return candidates.sorted( - (a, b) { - final cmp = a.asset.type.index - b.asset.type.index; - if (cmp != 0) return cmp; - return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); - }, - ); + return candidates.sorted((a, b) { + final cmp = a.asset.type.index - b.asset.type.index; + if (cmp != 0) return cmp; + return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); + }); } Future backupAsset( @@ -295,10 +285,7 @@ class BackupService { file = await asset.local!.loadFile(progressHandler: pmProgressHandler); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.loadFile( - withSubtype: true, - progressHandler: pmProgressHandler, - ); + livePhotoFile = await asset.local!.loadFile(withSubtype: true, progressHandler: pmProgressHandler); } } else { file = await asset.local!.originFile.timeout(const Duration(seconds: 5)); @@ -314,9 +301,7 @@ class BackupService { if (asset.local!.isLivePhoto) { if (livePhotoFile == null) { - _log.warning( - "Failed to obtain motion part of the livePhoto - $originalFileName", - ); + _log.warning("Failed to obtain motion part of the livePhoto - $originalFileName"); } } @@ -356,22 +341,14 @@ class BackupService { String? livePhotoVideoId; if (asset.local!.isLivePhoto && livePhotoFile != null) { - livePhotoVideoId = await uploadLivePhotoVideo( - originalFileName, - livePhotoFile, - baseRequest, - cancelToken, - ); + livePhotoVideoId = await uploadLivePhotoVideo(originalFileName, livePhotoFile, baseRequest, cancelToken); } if (livePhotoVideoId != null) { baseRequest.fields['livePhotoVideoId'] = livePhotoVideoId; } - final response = await httpClient.send( - baseRequest, - cancellationToken: cancelToken, - ); + final response = await httpClient.send(baseRequest, cancellationToken: cancelToken); final responseBody = jsonDecode(await response.stream.bytesToString()); @@ -417,10 +394,7 @@ class BackupService { ); if (shouldSyncAlbums) { - await _albumService.syncUploadAlbums( - candidate.albumNames, - [responseBody['id'] as String], - ); + await _albumService.syncUploadAlbums(candidate.albumNames, [responseBody['id'] as String]); } } } on http.CancelledException { @@ -459,10 +433,7 @@ class BackupService { if (livePhotoVideoFile == null) { return null; } - final livePhotoTitle = p.setExtension( - originalFileName, - p.extension(livePhotoVideoFile.path), - ); + final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoVideoFile.path)); final fileStream = livePhotoVideoFile.openRead(); final livePhotoRawUploadData = http.MultipartFile( "assetData", @@ -470,49 +441,36 @@ class BackupService { livePhotoVideoFile.lengthSync(), filename: livePhotoTitle, ); - final livePhotoReq = MultipartRequest( - baseRequest.method, - baseRequest.url, - onProgress: baseRequest.onProgress, - ) + final livePhotoReq = MultipartRequest(baseRequest.method, baseRequest.url, onProgress: baseRequest.onProgress) ..headers.addAll(baseRequest.headers) ..fields.addAll(baseRequest.fields); livePhotoReq.files.add(livePhotoRawUploadData); - var response = await httpClient.send( - livePhotoReq, - cancellationToken: cancelToken, - ); + var response = await httpClient.send(livePhotoReq, cancellationToken: cancelToken); var responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { var error = responseBody; - debugPrint( - "Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}", - ); + debugPrint("Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}"); } return responseBody.containsKey('id') ? responseBody['id'] : null; } String _getAssetType(AssetType assetType) => switch (assetType) { - AssetType.audio => "AUDIO", - AssetType.image => "IMAGE", - AssetType.video => "VIDEO", - AssetType.other => "OTHER", - }; + AssetType.audio => "AUDIO", + AssetType.image => "IMAGE", + AssetType.video => "VIDEO", + AssetType.other => "OTHER", + }; } class MultipartRequest extends http.MultipartRequest { /// Creates a new [MultipartRequest]. - MultipartRequest( - super.method, - super.url, { - required this.onProgress, - }); + MultipartRequest(super.method, super.url, {required this.onProgress}); final void Function(int bytes, int totalBytes) onProgress; diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 2f61a125ea..c0f3c0205e 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -37,11 +37,7 @@ class BackupVerificationService { /// Returns at most [limit] assets that were backed up without exif Future> findWronglyBackedUpAssets({int limit = 100}) async { final owner = _userService.getMyUser().id; - final List onlyLocal = await _assetRepository.getAll( - ownerId: owner, - state: AssetState.local, - limit: limit, - ); + final List onlyLocal = await _assetRepository.getAll(ownerId: owner, state: AssetState.local, limit: limit); final List remoteMatches = await _assetRepository.getMatches( assets: onlyLocal, ownerId: owner, @@ -75,41 +71,32 @@ class BackupVerificationService { if (deleteCandidates.length > 10) { // performs 2 checks in parallel for a nice speedup final half = deleteCandidates.length ~/ 2; - final lower = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(0, half), - originals: originals.slice(0, half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); - final upper = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(half), - originals: originals.slice(half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + final lower = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(0, half), + originals: originals.slice(0, half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); + final upper = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(half), + originals: originals.slice(half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); toDelete = await lower + await upper; } else { - toDelete = await compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates, - originals: originals, - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + toDelete = await compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates, + originals: originals, + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); } return toDelete; } @@ -122,7 +109,8 @@ class BackupVerificationService { String endpoint, RootIsolateToken rootIsolateToken, FileMediaRepository fileMediaRepository, - }) tuple, + }) + tuple, ) async { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; @@ -134,22 +122,14 @@ class BackupVerificationService { apiService.setEndpoint(tuple.endpoint); apiService.setAccessToken(tuple.auth); for (int i = 0; i < tuple.deleteCandidates.length; i++) { - if (await _compareAssets( - tuple.deleteCandidates[i], - tuple.originals[i], - apiService, - )) { + if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { result.add(tuple.deleteCandidates[i]); } } return result; } - static Future _compareAssets( - Asset remote, - Asset local, - ApiService apiService, - ) async { + static Future _compareAssets(Asset remote, Asset local, ApiService apiService) async { if (remote.checksum == local.checksum) return false; ExifInfo? exif = remote.exifInfo; if (exif != null && exif.latitude != null) return false; @@ -169,10 +149,7 @@ class BackupVerificationService { latLng.latitude != null && (remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) || remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) || - _sameExceptTimeZone( - remote.fileCreatedAt, - local.fileCreatedAt, - ))) { + _sameExceptTimeZone(remote.fileCreatedAt, local.fileCreatedAt))) { if (remote.type == AssetType.video) { // it's very unlikely that a video of same length, filesize, name // and date is wrong match. Cannot easily compare videos anyway diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index c08eacd0e3..1b717a6eeb 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -66,7 +66,6 @@ class DeepLinkService { return DeepLink([ // we need something to segue back to if the app was cold started // TODO: use MainTimelineRoute this when beta is default - if (isColdStart) (Store.isBetaTimelineEnabled) ? const MainTimelineRoute() : const PhotosRoute(), route, ]); @@ -96,10 +95,7 @@ class DeepLinkService { return _handleColdStart(deepLinkRoute, isColdStart); } - Future handleMyImmichApp( - PlatformDeepLink link, - bool isColdStart, - ) async { + Future handleMyImmichApp(PlatformDeepLink link, bool isColdStart) async { final path = link.uri.path; const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; @@ -152,10 +148,7 @@ class DeepLinkService { return null; } - return AssetViewerRoute( - initialIndex: 0, - timelineService: _betaTimelineFactory.fromAssets([asset]), - ); + return AssetViewerRoute(initialIndex: 0, timelineService: _betaTimelineFactory.fromAssets([asset])); } else { // TODO: Remove this when beta is default final asset = await _assetService.getAssetByRemoteId(assetId); @@ -166,12 +159,7 @@ class DeepLinkService { _currentAsset.set(asset); final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); - return GalleryViewerRoute( - renderList: renderList, - initialIndex: 0, - heroOffset: 0, - showStack: true, - ); + return GalleryViewerRoute(renderList: renderList, initialIndex: 0, heroOffset: 0, showStack: true); } } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index a540ca103f..7d2cf01b7c 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -14,10 +14,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( - (ref) => DownloadService( - ref.watch(fileMediaRepositoryProvider), - ref.watch(downloadRepositoryProvider), - ), + (ref) => DownloadService(ref.watch(fileMediaRepositoryProvider), ref.watch(downloadRepositoryProvider)), ); class DownloadService { @@ -29,10 +26,7 @@ class DownloadService { void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; void Function(TaskProgressUpdate)? onTaskProgress; - DownloadService( - this._fileMediaRepository, - this._downloadRepository, - ) { + DownloadService(this._fileMediaRepository, this._downloadRepository) { _downloadRepository.onImageDownloadStatus = _onImageDownloadCallback; _downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback; _downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback; @@ -82,11 +76,7 @@ class DownloadService { final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; final file = File(filePath); try { - final Asset? resultAsset = await _fileMediaRepository.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + final Asset? resultAsset = await _fileMediaRepository.saveVideo(file, title: title, relativePath: relativePath); return resultAsset != null; } catch (error, stack) { _log.severe("Error saving video", error, stack); @@ -98,10 +88,7 @@ class DownloadService { } } - Future saveLivePhotos( - Task task, - String livePhotosId, - ) async { + Future saveLivePhotos(Task task, String livePhotosId) async { final records = await _downloadRepository.getLiveVideoTasks(); if (records.length < 2) { return false; @@ -142,10 +129,7 @@ class DownloadService { await videoFile.delete(); } - await _downloadRepository.deleteRecordsWithIds([ - imageRecord.task.taskId, - videoRecord.task.taskId, - ]); + await _downloadRepository.deleteRecordsWithIds([imageRecord.task.taskId, videoRecord.task.taskId]); } } @@ -169,19 +153,13 @@ class DownloadService { asset.remoteId!, asset.fileName, group: kDownloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.image, - id: asset.remoteId!, - ).toJson(), + metadata: LivePhotosMetadata(part: LivePhotosPart.image, id: asset.remoteId!).toJson(), ), _buildDownloadTask( asset.livePhotoVideoId!, asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), group: kDownloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.video, - id: asset.remoteId!, - ).toJson(), + metadata: LivePhotosMetadata(part: LivePhotosPart.video, id: asset.remoteId!).toJson(), ), ]; } @@ -199,12 +177,7 @@ class DownloadService { ]; } - DownloadTask _buildDownloadTask( - String id, - String filename, { - String? group, - String? metadata, - }) { + DownloadTask _buildDownloadTask(String id, String filename, {String? group, String? metadata}) { final path = r'/assets/{id}/original'.replaceAll('{id}', id); final serverEndpoint = Store.get(StoreKey.serverEndpoint); final headers = ApiService.getRequestHeaders(); @@ -221,11 +194,7 @@ class DownloadService { } } -TaskRecord _findTaskRecord( - List records, - String livePhotosId, - LivePhotosPart part, -) { +TaskRecord _findTaskRecord(List records, String livePhotosId, LivePhotosPart part) { return records.firstWhere((record) { final metadata = LivePhotosMetadata.fromJson(record.task.metaData); return metadata.id == livePhotosId && metadata.part == part; diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index 468cc8f684..fe7358fce6 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -8,10 +8,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; class EntityService { final AssetRepository _assetRepository; final IsarUserRepository _isarUserRepository; - const EntityService( - this._assetRepository, - this._isarUserRepository, - ); + const EntityService(this._assetRepository, this._isarUserRepository); Future fillAlbumWithDatabaseEntities(Album album) async { final ownerId = album.ownerId; @@ -43,8 +40,5 @@ class EntityService { } final entityServiceProvider = Provider( - (ref) => EntityService( - ref.watch(assetRepositoryProvider), - ref.watch(userRepositoryProvider), - ), + (ref) => EntityService(ref.watch(assetRepositoryProvider), ref.watch(userRepositoryProvider)), ); diff --git a/mobile/lib/services/folder.service.dart b/mobile/lib/services/folder.service.dart index 3f4936bb61..91fb455110 100644 --- a/mobile/lib/services/folder.service.dart +++ b/mobile/lib/services/folder.service.dart @@ -6,9 +6,7 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; import 'package:immich_mobile/repositories/folder_api.repository.dart'; import 'package:logging/logging.dart'; -final folderServiceProvider = Provider( - (ref) => FolderService(ref.watch(folderApiRepositoryProvider)), -); +final folderServiceProvider = Provider((ref) => FolderService(ref.watch(folderApiRepositoryProvider))); class FolderService { final FolderApiRepository _folderApiRepository; @@ -44,11 +42,7 @@ class FolderService { if (!folderMap[parentPath]!.any((f) => f.name == segments[i])) { folderMap[parentPath]!.add( - RecursiveFolder( - path: parentPath == '_root_' ? '' : parentPath, - name: segments[i], - subfolders: [], - ), + RecursiveFolder(path: parentPath == '_root_' ? '' : parentPath, name: segments[i], subfolders: []), ); // Sort folders based on order parameter folderMap[parentPath]!.sort( @@ -64,9 +58,7 @@ class FolderService { if (folderMap.containsKey(fullPath)) { folder.subfolders.addAll(folderMap[fullPath]!); // Sort subfolders based on order parameter - folder.subfolders.sort( - (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), - ); + folder.subfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var subfolder in folder.subfolders) { attachSubfolders(subfolder); } @@ -75,24 +67,16 @@ class FolderService { List rootSubfolders = folderMap['_root_'] ?? []; // Sort root subfolders based on order parameter - rootSubfolders.sort( - (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), - ); + rootSubfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var folder in rootSubfolders) { attachSubfolders(folder); } - return RootFolder( - subfolders: rootSubfolders, - path: '/', - ); + return RootFolder(subfolders: rootSubfolders, path: '/'); } - Future> getFolderAssets( - RootFolder folder, - SortOrder order, - ) async { + Future> getFolderAssets(RootFolder folder, SortOrder order) async { try { if (folder is RecursiveFolder) { String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; @@ -110,11 +94,7 @@ class FolderService { final result = await _folderApiRepository.getAssetsForPath('/'); return result; } catch (e, stack) { - _log.severe( - "Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", - e, - stack, - ); + _log.severe("Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", e, stack); return []; } } diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index de9b8bbcb2..dcf7685237 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -41,11 +41,7 @@ class GCastService { void Function(CastState)? onCastState; - GCastService( - this._gCastRepository, - this._sessionsApiService, - this._assetApiRepository, - ) { + GCastService(this._gCastRepository, this._sessionsApiService, this._assetApiRepository) { _gCastRepository.onCastStatus = _onCastStatusCallback; _gCastRepository.onCastMessage = _onCastMessageCallback; } @@ -100,9 +96,7 @@ class GCastService { } if (status["media"] != null && status["media"]["duration"] != null) { - final duration = Duration( - milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt(), - ); + final duration = Duration(milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt()); onDuration?.call(duration); } @@ -170,13 +164,8 @@ class GCastService { } final unauthenticatedUrl = asset.isVideo - ? getPlaybackUrlForRemoteId( - asset.id, - ) - : getThumbnailUrlForRemoteId( - asset.id, - type: AssetMediaSize.fullsize, - ); + ? getPlaybackUrlForRemoteId(asset.id) + : getThumbnailUrlForRemoteId(asset.id, type: AssetMediaSize.fullsize); final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; @@ -220,17 +209,11 @@ class GCastService { } void play() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PLAY", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PLAY", "mediaSessionId": _sessionId}); } void pause() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PAUSE", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PAUSE", "mediaSessionId": _sessionId}); } void seekTo(Duration position) { @@ -242,10 +225,7 @@ class GCastService { } void stop() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "STOP", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "STOP", "mediaSessionId": _sessionId}); _mediaStatusPollingTimer?.cancel(); currentAssetId = null; @@ -258,14 +238,13 @@ class GCastService { final dests = await _gCastRepository.listDestinations(); return dests - .map( - (device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device), - ) + .map((device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device)) .where((device) { - final caString = device.$3.extras["ca"]; - final caNumber = int.tryParse(caString ?? "0") ?? 0; + final caString = device.$3.extras["ca"]; + final caNumber = int.tryParse(caString ?? "0") ?? 0; - return isDisplay(caNumber); - }).toList(growable: false); + return isDisplay(caNumber); + }) + .toList(growable: false); } } diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart index f0554bf00b..48302be79c 100644 --- a/mobile/lib/services/hash.service.dart +++ b/mobile/lib/services/hash.service.dart @@ -17,8 +17,8 @@ class HashService { required BackgroundService backgroundService, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _deviceAssetRepository = deviceAssetRepository, - _backgroundService = backgroundService; + }) : _deviceAssetRepository = deviceAssetRepository, + _backgroundService = backgroundService; final IsarDeviceAssetRepository _deviceAssetRepository; final BackgroundService _backgroundService; @@ -33,9 +33,7 @@ class HashService { assets.sort(Asset.compareByLocalId); // Get and sort DB entries - guaranteed to be a subset of assets - final hashesInDB = await _deviceAssetRepository.getByIds( - assets.map((a) => a.localId!).toList(), - ); + final hashesInDB = await _deviceAssetRepository.getByIds(assets.map((a) => a.localId!).toList()); hashesInDB.sort((a, b) => a.assetId.compareTo(b.assetId)); int dbIndex = 0; @@ -60,9 +58,7 @@ class HashService { matchingDbEntry.hash.isNotEmpty && matchingDbEntry.modifiedTime.isAtSameMomentAs(asset.fileModifiedAt)) { // Reuse the existing hash - hashedAssets.add( - asset.copyWith(checksum: base64.encode(matchingDbEntry.hash)), - ); + hashedAssets.add(asset.copyWith(checksum: base64.encode(matchingDbEntry.hash))); continue; } @@ -125,10 +121,7 @@ class HashService { /// Processes a batch of files and returns a list of successfully hashed assets after saving /// them in [DeviceAssetToHash] for future retrieval - Future> _processBatch( - List<_AssetPath> toBeHashed, - List toBeDeleted, - ) async { + Future> _processBatch(List<_AssetPath> toBeHashed, List toBeDeleted) async { _log.info("Hashing ${toBeHashed.length} files"); final hashes = await _hashFiles(toBeHashed.map((e) => e.path).toList()); assert( @@ -143,13 +136,7 @@ class HashService { final asset = toBeHashed.elementAtOrNull(index)?.asset; if (asset != null && hash?.length == 20) { hashedAssets.add(asset.copyWith(checksum: base64.encode(hash!))); - toBeAdded.add( - DeviceAsset( - assetId: asset.localId!, - hash: hash, - modifiedTime: asset.fileModifiedAt, - ), - ); + toBeAdded.add(DeviceAsset(assetId: asset.localId!, hash: hash, modifiedTime: asset.fileModifiedAt)); } else { _log.warning("Failed to hash file ${asset?.localId ?? ''}"); if (asset != null) { diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart index 12da5f256b..4721911e8d 100644 --- a/mobile/lib/services/local_auth.service.dart +++ b/mobile/lib/services/local_auth.service.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/repositories/biometric.repository.dart'; -final localAuthServiceProvider = Provider( - (ref) => LocalAuthService( - ref.watch(biometricRepositoryProvider), - ), -); +final localAuthServiceProvider = Provider((ref) => LocalAuthService(ref.watch(biometricRepositoryProvider))); class LocalAuthService { final BiometricRepository _biometricRepository; diff --git a/mobile/lib/services/local_files_manager.service.dart b/mobile/lib/services/local_files_manager.service.dart index ae935a131c..7cb3067342 100644 --- a/mobile/lib/services/local_files_manager.service.dart +++ b/mobile/lib/services/local_files_manager.service.dart @@ -2,9 +2,7 @@ import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; -final localFileManagerServiceProvider = Provider( - (ref) => const LocalFilesManagerService(), -); +final localFileManagerServiceProvider = Provider((ref) => const LocalFilesManagerService()); class LocalFilesManagerService { const LocalFilesManagerService(); @@ -22,10 +20,7 @@ class LocalFilesManagerService { Future restoreFromTrash(String fileName, int type) async { try { - return await _channel.invokeMethod( - 'restoreFromTrash', - {'fileName': fileName, 'type': type}, - ); + return await _channel.invokeMethod('restoreFromTrash', {'fileName': fileName, 'type': type}); } catch (e, s) { _logger.warning('Error restore file from trash', e, s); return false; diff --git a/mobile/lib/services/local_notification.service.dart b/mobile/lib/services/local_notification.service.dart index d67cc23616..e7fc3292e2 100644 --- a/mobile/lib/services/local_notification.service.dart +++ b/mobile/lib/services/local_notification.service.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:permission_handler/permission_handler.dart'; final localNotificationService = Provider( - (ref) => LocalNotificationService( - ref.watch(notificationPermissionProvider), - ref, - ), + (ref) => LocalNotificationService(ref.watch(notificationPermissionProvider), ref), ); class LocalNotificationService { @@ -46,10 +43,7 @@ class LocalNotificationService { AndroidNotificationDetails androidNotificationDetails, DarwinNotificationDetails iosNotificationDetails, ) async { - final notificationDetails = NotificationDetails( - android: androidNotificationDetails, - iOS: iosNotificationDetails, - ); + final notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails); if (_permissionStatus == PermissionStatus.granted) { await _localNotificationPlugin.show(id, title, body, notificationDetails); @@ -95,20 +89,12 @@ class LocalNotificationService { ongoing: true, actions: (showActions ?? false) ? [ - const AndroidNotificationAction( - cancelUploadActionID, - 'Cancel', - showsUserInterface: true, - ), + const AndroidNotificationAction(cancelUploadActionID, 'Cancel', showsUserInterface: true), ] : null, ) // Non-progress notification - : AndroidNotificationDetails( - androidChannelID, - androidChannelName, - playSound: false, - ); + : AndroidNotificationDetails(androidChannelID, androidChannelName, playSound: false); final iosNotificationDetails = DarwinNotificationDetails( presentBadge: true, @@ -116,18 +102,10 @@ class LocalNotificationService { presentBanner: presentBanner, ); - return _showOrUpdateNotification( - notificationlId, - title, - body, - androidNotificationDetails, - iosNotificationDetails, - ); + return _showOrUpdateNotification(notificationlId, title, body, androidNotificationDetails, iosNotificationDetails); } - void _onDidReceiveForegroundNotificationResponse( - NotificationResponse notificationResponse, - ) { + void _onDidReceiveForegroundNotificationResponse(NotificationResponse notificationResponse) { // Handle notification actions switch (notificationResponse.actionId) { case cancelUploadActionID: diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index ca877e7c78..e485bb0957 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final memoryServiceProvider = StateProvider((ref) { - return MemoryService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ); + return MemoryService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)); }); class MemoryService { @@ -38,17 +35,8 @@ class MemoryService { final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); - memories.add( - Memory( - title: title, - assets: dbAssets, - ), - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); + memories.add(Memory(title: title, assets: dbAssets)); } } @@ -72,16 +60,9 @@ class MemoryService { return null; } final yearsAgo = DateTime.now().year - memoryResponse.data.year; - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); - return Memory( - title: title, - assets: dbAssets, - ); + return Memory(title: title, assets: dbAssets); } catch (error, stack) { log.severe("Cannot get memory with ID: $id", error, stack); return null; diff --git a/mobile/lib/services/network.service.dart b/mobile/lib/services/network.service.dart index de55da8d7c..8622400e7a 100644 --- a/mobile/lib/services/network.service.dart +++ b/mobile/lib/services/network.service.dart @@ -3,10 +3,7 @@ import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; final networkServiceProvider = Provider((ref) { - return NetworkService( - ref.watch(networkRepositoryProvider), - ref.watch(permissionRepositoryProvider), - ); + return NetworkService(ref.watch(networkRepositoryProvider), ref.watch(permissionRepositoryProvider)); }); class NetworkService { diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index 9a54a8d7c9..99ceca3229 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -11,24 +11,14 @@ class OAuthService { final log = Logger('OAuthService'); OAuthService(this._apiService); - Future getOAuthServerUrl( - String serverUrl, - String state, - String codeChallenge, - ) async { + Future getOAuthServerUrl(String serverUrl, String state, String codeChallenge) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); final redirectUri = '$callbackUrlScheme:///oauth-callback'; - log.info( - "Starting OAuth flow with redirect URI: $redirectUri", - ); + 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: state, codeChallenge: codeChallenge), ); final authUrl = dto?.url; @@ -37,31 +27,17 @@ class OAuthService { return authUrl; } - Future oAuthLogin( - String oauthUrl, - String state, - String codeVerifier, - ) async { - String result = await FlutterWebAuth2.authenticate( - url: oauthUrl, - callbackUrlScheme: callbackUrlScheme, - ); + Future oAuthLogin(String oauthUrl, String state, String codeVerifier) async { + String result = await FlutterWebAuth2.authenticate(url: oauthUrl, callbackUrlScheme: callbackUrlScheme); log.info('Received OAuth callback: $result'); if (result.startsWith('app.immich:/oauth-callback')) { - result = result.replaceAll( - 'app.immich:/oauth-callback', - 'app.immich:///oauth-callback', - ); + result = result.replaceAll('app.immich:/oauth-callback', 'app.immich:///oauth-callback'); } return await _apiService.oAuthApi.finishOAuth( - OAuthCallbackDto( - url: result, - state: state, - codeVerifier: codeVerifier, - ), + OAuthCallbackDto(url: result, state: state, codeVerifier: codeVerifier), ); } } diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index 7b4f8a09b0..b8e5ae9a4d 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -20,11 +20,7 @@ class PartnerService { final IsarUserRepository _isarUserRepository; final Logger _log = Logger("PartnerService"); - PartnerService( - this._partnerApiRepository, - this._isarUserRepository, - this._partnerRepository, - ); + PartnerService(this._partnerApiRepository, this._isarUserRepository, this._partnerRepository); Future> getSharedWith() async { return _partnerRepository.getSharedWith(); @@ -64,15 +60,9 @@ class PartnerService { return false; } - Future updatePartner( - UserDto partner, { - required bool inTimeline, - }) async { + Future updatePartner(UserDto partner, {required bool inTimeline}) async { try { - final dto = await _partnerApiRepository.update( - partner.id, - inTimeline: inTimeline, - ); + final dto = await _partnerApiRepository.update(partner.id, inTimeline: inTimeline); await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index a2f7203d37..37b16a8d29 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -11,10 +11,10 @@ part 'person.service.g.dart'; @riverpod PersonService personService(Ref ref) => PersonService( - ref.watch(personApiRepositoryProvider), - ref.watch(assetApiRepositoryProvider), - ref.read(assetRepositoryProvider), - ); + ref.watch(personApiRepositoryProvider), + ref.watch(assetApiRepositoryProvider), + ref.read(assetRepositoryProvider), +); class PersonService { final Logger _log = Logger("PersonService"); @@ -22,11 +22,7 @@ class PersonService { final AssetApiRepository _assetApiRepository; final AssetRepository _assetRepository; - PersonService( - this._personApiRepository, - this._assetApiRepository, - this._assetRepository, - ); + PersonService(this._personApiRepository, this._assetApiRepository, this._assetRepository); Future> getAllPeople() async { try { diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index 2aa39fcf80..250fb67d82 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -25,11 +25,7 @@ class SearchService { final SearchApiRepository _searchApiRepository; final _log = Logger("SearchService"); - SearchService( - this._apiService, - this._assetRepository, - this._searchApiRepository, - ); + SearchService(this._apiService, this._assetRepository, this._searchApiRepository); Future?> getSearchSuggestions( SearchSuggestionType type, { diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart index 38e6deb0d4..95d2e7a2c8 100644 --- a/mobile/lib/services/secure_storage.service.dart +++ b/mobile/lib/services/secure_storage.service.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/secure_storage.repository.dart'; final secureStorageServiceProvider = Provider( - (ref) => SecureStorageService( - ref.watch(secureStorageRepositoryProvider), - ), + (ref) => SecureStorageService(ref.watch(secureStorageRepositoryProvider)), ); class SecureStorageService { diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index 75ce68a73c..4319d9dbae 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -7,11 +7,7 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; -final serverInfoServiceProvider = Provider( - (ref) => ServerInfoService( - ref.watch(apiServiceProvider), - ), -); +final serverInfoServiceProvider = Provider((ref) => ServerInfoService(ref.watch(apiServiceProvider))); class ServerInfoService { final ApiService _apiService; diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index fec8b76af0..7ba385d71c 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -39,10 +39,7 @@ class ShareService { final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!); if (res.statusCode != 200) { - _log.severe( - "Asset download for ${asset.fileName} failed", - res.toLoggerString(), - ); + _log.severe("Asset download for ${asset.fileName} failed", res.toLoggerString()); continue; } @@ -57,18 +54,13 @@ class ShareService { } if (downloadedXFiles.length != assets.length) { - _log.warning( - "Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}", - ); + _log.warning("Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}"); } final size = MediaQuery.of(context).size; Share.shareXFiles( downloadedXFiles, - sharePositionOrigin: Rect.fromPoints( - Offset.zero, - Offset(size.width / 3, size.height), - ), + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), ); return true; } catch (error) { diff --git a/mobile/lib/services/share_intent_service.dart b/mobile/lib/services/share_intent_service.dart index e514e5bbdc..fca5c4a188 100644 --- a/mobile/lib/services/share_intent_service.dart +++ b/mobile/lib/services/share_intent_service.dart @@ -2,19 +2,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/repositories/share_handler.repository.dart'; -final shareIntentServiceProvider = Provider( - (ref) => ShareIntentService( - ref.watch(shareHandlerRepositoryProvider), - ), -); +final shareIntentServiceProvider = Provider((ref) => ShareIntentService(ref.watch(shareHandlerRepositoryProvider))); class ShareIntentService { final ShareHandlerRepository shareHandlerRepository; void Function(List attachments)? onSharedMedia; - ShareIntentService( - this.shareHandlerRepository, - ); + ShareIntentService(this.shareHandlerRepository); void init() { shareHandlerRepository.onSharedMedia = onSharedMedia; diff --git a/mobile/lib/services/shared_link.service.dart b/mobile/lib/services/shared_link.service.dart index 44923b39d7..25151c234f 100644 --- a/mobile/lib/services/shared_link.service.dart +++ b/mobile/lib/services/shared_link.service.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final sharedLinkServiceProvider = Provider( - (ref) => SharedLinkService(ref.watch(apiServiceProvider)), -); +final sharedLinkServiceProvider = Provider((ref) => SharedLinkService(ref.watch(apiServiceProvider))); class SharedLinkService { final ApiService _apiService; diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart index d46c0c0b99..c24b9fb7f8 100644 --- a/mobile/lib/services/stack.service.dart +++ b/mobile/lib/services/stack.service.dart @@ -23,24 +23,16 @@ class StackService { Future createStack(List assetIds) async { try { - return _api.stacksApi.createStack( - StackCreateDto(assetIds: assetIds), - ); + return _api.stacksApi.createStack(StackCreateDto(assetIds: assetIds)); } catch (error) { debugPrint("Error while creating stack: $error"); } return null; } - Future updateStack( - String stackId, - String primaryAssetId, - ) async { + Future updateStack(String stackId, String primaryAssetId) async { try { - return await _api.stacksApi.updateStack( - stackId, - StackUpdateDto(primaryAssetId: primaryAssetId), - ); + return await _api.stacksApi.updateStack(stackId, StackUpdateDto(primaryAssetId: primaryAssetId)); } catch (error) { debugPrint("Error while updating stack children: $error"); } @@ -68,8 +60,5 @@ class StackService { } final stackServiceProvider = Provider( - (ref) => StackService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ), + (ref) => StackService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)), ); diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 723dc9c34b..7b420413cf 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -100,38 +100,26 @@ class SyncService { /// Returns `true` if there were any changes Future syncRemoteAssetsToDb({ required List users, - required Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + required Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, required FutureOr?> Function(UserDto user, DateTime until) loadAssets, - }) => - _lock.run( - () async => - await _syncRemoteAssetChanges(users, getChangedAssets) ?? - await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), - ); + }) => _lock.run( + () async => + await _syncRemoteAssetChanges(users, getChangedAssets) ?? + await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), + ); /// Syncs remote albums to the database /// returns `true` if there were any changes - Future syncRemoteAlbumsToDb( - List remote, - ) => - _lock.run(() => _syncRemoteAlbumsToDb(remote)); + Future syncRemoteAlbumsToDb(List remote) => _lock.run(() => _syncRemoteAlbumsToDb(remote)); /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) => + Future syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) => _lock.run(() => _syncLocalAlbumAssetsToDb(onDevice, excludedAssets)); /// returns all Asset IDs that are not contained in the existing list - List sharedAssetsToRemove( - List deleteCandidates, - List existing, - ) { + List sharedAssetsToRemove(List deleteCandidates, List existing) { if (deleteCandidates.isEmpty) { return []; } @@ -200,10 +188,8 @@ class SyncService { /// Efficiently syncs assets via changes. Returns `null` when a full sync is required. Future _syncRemoteAssetChanges( List users, - Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, ) async { final currentUser = _userService.getMyUser(); final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); @@ -237,9 +223,7 @@ class SyncService { final List localAssets = await _assetRepository.getAllLocal(); final List matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList(); - final mediaUrls = await Future.wait( - matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), - ); + final mediaUrls = await Future.wait(matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null))); await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } @@ -247,18 +231,9 @@ class SyncService { /// Deletes remote-only assets, updates merged assets to be local-only Future handleRemoteAssetRemoval(List idsToDelete) async { return _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - idsToDelete, - state: AssetState.remote, - ); - final merged = await _assetRepository.getAllByRemoteId( - idsToDelete, - state: AssetState.merged, - ); - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + await _assetRepository.deleteAllByRemoteId(idsToDelete, state: AssetState.remote); + final merged = await _assetRepository.getAllByRemoteId(idsToDelete, state: AssetState.merged); + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { await _moveToTrashMatchedAssets(idsToDelete); } if (merged.isEmpty) return; @@ -304,10 +279,7 @@ class SyncService { if (remote == null) { return false; } - final List inDb = await _assetRepository.getAll( - ownerId: user.id, - sortBy: AssetSort.checksum, - ); + final List inDb = await _assetRepository.getAll(ownerId: user.id, sortBy: AssetSort.checksum); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); remote.sort(Asset.compareByChecksum); @@ -343,15 +315,10 @@ class SyncService { /// Syncs remote albums to the database /// returns `true` if there were any changes - Future _syncRemoteAlbumsToDb( - List remoteAlbums, - ) async { + Future _syncRemoteAlbumsToDb(List remoteAlbums) async { remoteAlbums.sortBy((e) => e.remoteId!); - final List dbAlbums = await _albumRepository.getAll( - remote: true, - sortBy: AlbumSort.remoteId, - ); + final List dbAlbums = await _albumRepository.getAll(remote: true, sortBy: AlbumSort.remoteId); final List toDelete = []; final List existing = []; @@ -379,12 +346,7 @@ class SyncService { /// syncs albums from the server to the local database (does not support /// syncing changes from local back to server) /// accumulates - Future _syncRemoteAlbum( - Album dto, - Album album, - List deleteCandidates, - List existing, - ) async { + Future _syncRemoteAlbum(Album dto, Album album, List deleteCandidates, List existing) async { if (!_hasRemoteAlbumChanged(dto, album)) { return false; } @@ -393,18 +355,11 @@ class SyncService { final originalDto = dto; dto = await _albumApiRepository.get(dto.remoteId!); - final assetsInDb = await _assetRepository.getByAlbum( - album, - sortBy: AssetSort.ownerIdChecksum, - ); + final assetsInDb = await _assetRepository.getByAlbum(album, sortBy: AssetSort.ownerIdChecksum); assert(assetsInDb.isSorted(Asset.compareByOwnerChecksum), "inDb unsorted!"); final List assetsOnRemote = dto.remoteAssets.toList(); assetsOnRemote.sort(Asset.compareByOwnerChecksum); - final (toAdd, toUpdate, toUnlink) = _diffAssets( - assetsOnRemote, - assetsInDb, - compare: Asset.compareByOwnerChecksum, - ); + final (toAdd, toUpdate, toUnlink) = _diffAssets(assetsOnRemote, assetsInDb, compare: Asset.compareByOwnerChecksum); // update shared users final List sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false); @@ -476,10 +431,7 @@ class SyncService { /// Adds a remote album to the database while making sure to add any foreign /// (shared) assets to the database beforehand /// accumulates assets already existing in the database - Future _addAlbumFromServer( - Album album, - List existing, - ) async { + Future _addAlbumFromServer(Album album, List existing) async { if (album.remoteAssetCount != album.remoteAssets.length) { album = await _albumApiRepository.get(album.remoteId!); } @@ -493,23 +445,20 @@ class SyncService { await _entityService.fillAlbumWithDatabaseEntities(album); await _albumRepository.create(album); } else { - _log.warning("Failed to add album from server: assetCount ${album.remoteAssetCount} != " - "asset array length ${album.remoteAssets.length} for album ${album.name}"); + _log.warning( + "Failed to add album from server: assetCount ${album.remoteAssetCount} != " + "asset array length ${album.remoteAssets.length} for album ${album.name}", + ); } } /// Accumulates all suitable album assets to the `deleteCandidates` and /// removes the album from the database. - Future _removeAlbumFromDb( - Album album, - List deleteCandidates, - ) async { + Future _removeAlbumFromDb(Album album, List deleteCandidates) async { if (album.isLocal) { _log.info("Removing local album $album from DB"); // delete assets in DB unless they are remote or part of some other album - deleteCandidates.addAll( - await _assetRepository.getByAlbum(album, state: AssetState.local), - ); + deleteCandidates.addAll(await _assetRepository.getByAlbum(album, state: AssetState.local)); } else if (album.shared) { // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner final userIds = (await _getAllAccessibleUsers()).map((user) => user.id); @@ -526,10 +475,7 @@ class SyncService { /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future _syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) async { + Future _syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) async { onDevice.sort((a, b) => a.localId!.compareTo(b.localId!)); final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); final List deleteCandidates = []; @@ -538,31 +484,19 @@ class SyncService { onDevice, inDb, compare: (Album a, Album b) => a.localId!.compareTo(b.localId!), - both: (Album a, Album b) => _syncAlbumInDbAndOnDevice( - a, - b, - deleteCandidates, - existing, - excludedAssets, - ), + both: (Album a, Album b) => _syncAlbumInDbAndOnDevice(a, b, deleteCandidates, existing, excludedAssets), onlyFirst: (Album a) => _addAlbumFromDevice(a, existing, excludedAssets), onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates), ); - _log.fine( - "Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete", - ); + _log.fine("Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete"); final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false); - _log.fine( - "${toDelete.length} assets to delete, ${toUpdate.length} to update", - ); + _log.fine("${toDelete.length} assets to delete, ${toUpdate.length} to update"); if (toDelete.isNotEmpty || toUpdate.isNotEmpty) { await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); }); - _log.info( - "Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB", - ); + _log.info("Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB"); } return anyChanges; } @@ -580,9 +514,7 @@ class SyncService { ]) async { _log.info("Syncing a local album to DB: ${deviceAlbum.name}"); if (!forceRefresh && !await _hasAlbumChangeOnDevice(deviceAlbum, dbAlbum)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } _log.info("Local album ${deviceAlbum.name} has changed. Syncing..."); @@ -599,10 +531,7 @@ class SyncService { assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final List onDevice = await _getHashedAssets( - deviceAlbum, - excludedAssets: excludedAssets, - ); + final List onDevice = await _getHashedAssets(deviceAlbum, excludedAssets: excludedAssets); _removeDuplicates(onDevice); // _removeDuplicates sorts `onDevice` by checksum final (toAdd, toUpdate, toDelete) = _diffAssets(onDevice, inDb); @@ -613,16 +542,9 @@ class SyncService { dbAlbum.description == deviceAlbum.description && dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) { // changes only affeted excluded albums - _log.info( - "Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.", - ); + _log.info("Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync."); if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) { - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); } return false; } @@ -648,12 +570,7 @@ class SyncService { await _albumRepository.removeAssets(dbAlbum, toDelete); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); }); _log.info("Synced changes of local album ${deviceAlbum.name} to DB"); } catch (e) { @@ -667,17 +584,13 @@ class SyncService { /// returns `true` if successful, else `false` Future _syncDeviceAlbumFast(Album deviceAlbum, Album dbAlbum) async { if (!deviceAlbum.modifiedAt.isAfter(dbAlbum.modifiedAt)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0; if (totalOnDevice <= lastKnownTotal) { - _log.info( - "Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync."); return false; } final List newAssets = await _getHashedAssets( @@ -701,16 +614,11 @@ class SyncService { await _albumRepository.addAssets(dbAlbum, existingInDb + updated); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll( - [ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)], - ); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)]); }); _log.info("Fast synced local album ${deviceAlbum.name} to DB"); } catch (e) { - _log.severe( - "Failed to fast sync local album ${deviceAlbum.name} to DB", - e, - ); + _log.severe("Failed to fast sync local album ${deviceAlbum.name} to DB", e); return false; } @@ -719,21 +627,12 @@ class SyncService { /// Adds a new album from the device to the database and Accumulates all /// assets already existing in the database to the list of `existing` assets - Future _addAlbumFromDevice( - Album album, - List existing, [ - Set? excludedAssets, - ]) async { + Future _addAlbumFromDevice(Album album, List existing, [Set? excludedAssets]) async { _log.info("Adding a new local album to DB: ${album.name}"); - final assets = await _getHashedAssets( - album, - excludedAssets: excludedAssets, - ); + final assets = await _getHashedAssets(album, excludedAssets: excludedAssets); _removeDuplicates(assets); final (existingInDb, updated) = await _linkWithExistingFromDb(assets); - _log.info( - "${existingInDb.length} assets already existed in DB, to upsert ${updated.length}", - ); + _log.info("${existingInDb.length} assets already existed in DB, to upsert ${updated.length}"); await upsertAssetsWithExif(updated); existing.addAll(existingInDb); album.assets.addAll(existingInDb); @@ -743,9 +642,7 @@ class SyncService { try { await _albumRepository.create(album); final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!); - await _eTagRepository.upsertAll([ - ETag(id: album.eTagKeyAssetCount, assetCount: assetCount), - ]); + await _eTagRepository.upsertAll([ETag(id: album.eTagKeyAssetCount, assetCount: assetCount)]); _log.info("Added a new local album to DB: ${album.name}"); } catch (e) { _log.severe("Failed to add new local album ${album.name} to DB", e); @@ -753,9 +650,7 @@ class SyncService { } /// Returns a tuple (existing, updated) - Future<(List existing, List updated)> _linkWithExistingFromDb( - List assets, - ) async { + Future<(List existing, List updated)> _linkWithExistingFromDb(List assets) async { if (assets.isEmpty) return ([].cast(), [].cast()); final List inDb = await _assetRepository.getAllByOwnerIdChecksum( @@ -789,17 +684,12 @@ class SyncService { if (asset.isTrashed) { final mediaUrl = await asset.local?.getMediaUrl(); if (mediaUrl == null) { - _log.warning( - "Failed to get media URL for asset ${asset.name} while moving to trash", - ); + _log.warning("Failed to get media URL for asset ${asset.name} while moving to trash"); continue; } trashMediaUrls.add(mediaUrl); } else { - await _localFilesManager.restoreFromTrash( - asset.fileName, - asset.type.index, - ); + await _localFilesManager.restoreFromTrash(asset.fileName, asset.type.index); } } @@ -812,10 +702,7 @@ class SyncService { Future upsertAssetsWithExif(List assets) async { if (assets.isEmpty) return; - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { _toggleTrashStatusForAssets(assets); } @@ -842,21 +729,15 @@ class SyncService { final Asset? b = inDb[i]; if (b == null) { if (!a.isInDb) { - _log.warning( - "Trying to update an asset that does not exist in DB:\n$a", - ); + _log.warning("Trying to update an asset that does not exist in DB:\n$a"); } } else if (a.id != b.id) { - _log.warning( - "Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a", - ); + _log.warning("Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a"); } } for (int i = 1; i < assets.length; i++) { if (Asset.compareByOwnerChecksum(assets[i - 1], assets[i]) == 0) { - _log.warning( - "Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}", - ); + _log.warning("Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}"); } } } @@ -878,18 +759,16 @@ class SyncService { modifiedFrom: modifiedFrom, modifiedUntil: modifiedUntil, ); - final filtered = - excludedAssets == null ? entities : entities.where((e) => !excludedAssets.contains(e.localId!)).toList(); + final filtered = excludedAssets == null + ? entities + : entities.where((e) => !excludedAssets.contains(e.localId!)).toList(); return _hashService.hashAssets(filtered); } List _removeDuplicates(List assets) { final int before = assets.length; assets.sort(Asset.compareByOwnerChecksumCreatedModified); - assets.uniqueConsecutive( - compare: Asset.compareByOwnerChecksum, - onDuplicate: (a, b) => {}, - ); + assets.uniqueConsecutive(compare: Asset.compareByOwnerChecksum, onDuplicate: (a, b) => {}); final int duplicates = before - assets.length; if (duplicates > 0) { _log.warning("Ignored $duplicates duplicate assets on device"); @@ -898,10 +777,7 @@ class SyncService { } /// returns `true` if the albums differ on the surface - Future _hasAlbumChangeOnDevice( - Album deviceAlbum, - Album dbAlbum, - ) async { + Future _hasAlbumChangeOnDevice(Album deviceAlbum, Album dbAlbum) async { return deviceAlbum.name != dbAlbum.name || deviceAlbum.description != dbAlbum.description || !deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || @@ -966,9 +842,7 @@ class SyncService { sharedWith, compare: (UserDto a, UserDto b) => a.id.compareTo(b.id), both: (UserDto a, UserDto b) { - updatedSharedWith.add( - a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true), - ); + updatedSharedWith.add(a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true)); return true; }, onlyFirst: (UserDto a) => updatedSharedWith.add(a), @@ -1065,8 +939,5 @@ bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) { !remoteAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || !isAtSameMomentAs(remoteAlbum.startDate, dbAlbum.startDate) || !isAtSameMomentAs(remoteAlbum.endDate, dbAlbum.endDate) || - !isAtSameMomentAs( - remoteAlbum.lastModifiedAssetTimestamp, - dbAlbum.lastModifiedAssetTimestamp, - ); + !isAtSameMomentAs(remoteAlbum.lastModifiedAssetTimestamp, dbAlbum.lastModifiedAssetTimestamp); } diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 1a4f9e2685..eaff1027d8 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -21,11 +21,7 @@ class TimelineService { final AppSettingsService _appSettingsService; final UserService _userService; - const TimelineService( - this._timelineRepository, - this._appSettingsService, - this._userService, - ); + const TimelineService(this._timelineRepository, this._appSettingsService, this._userService); Future> getTimelineUserIds() async { final me = _userService.getMyUser(); @@ -42,10 +38,7 @@ class TimelineService { } Stream watchMultiUsersTimeline(List userIds) { - return _timelineRepository.watchMultiUsersTimeline( - userIds, - _getGroupByOption(), - ); + return _timelineRepository.watchMultiUsersTimeline(userIds, _getGroupByOption()); } Stream watchArchiveTimeline() async* { @@ -61,10 +54,7 @@ class TimelineService { } Stream watchAlbumTimeline(Album album) async* { - yield* _timelineRepository.watchAlbumTimeline( - album, - _getGroupByOption(), - ); + yield* _timelineRepository.watchAlbumTimeline(album, _getGroupByOption()); } Stream watchTrashTimeline() async* { @@ -79,10 +69,7 @@ class TimelineService { return _timelineRepository.watchAllVideosTimeline(user.id); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy? groupBy, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy? groupBy) { GroupAssetsBy groupOption = GroupAssetsBy.none; if (groupBy == null) { groupOption = _getGroupByOption(); @@ -90,10 +77,7 @@ class TimelineService { groupOption = groupBy; } - return _timelineRepository.getTimelineFromAssets( - assets, - groupOption, - ); + return _timelineRepository.getTimelineFromAssets(assets, groupOption); } Stream watchAssetSelectionTimeline() async* { @@ -109,9 +93,6 @@ class TimelineService { Stream watchLockedTimelineProvider() async* { final user = _userService.getMyUser(); - yield* _timelineRepository.watchLockedTimeline( - user.id, - _getGroupByOption(), - ); + yield* _timelineRepository.watchLockedTimeline(user.id, _getGroupByOption()); } } diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 6cd7dfc641..2c51a68c59 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -20,17 +20,11 @@ class TrashService { final AssetRepository _assetRepository; final UserService _userService; - const TrashService( - this._apiService, - this._assetRepository, - this._userService, - ); + const TrashService(this._apiService, this._assetRepository, this._userService); Future restoreAssets(Iterable assetList) async { final remoteAssets = assetList.where((a) => a.isRemote); - await _apiService.trashApi.restoreAssets( - BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList()), - ); + await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList())); final updatedAssets = remoteAssets.map((asset) { asset.isTrashed = false; @@ -49,15 +43,9 @@ class TrashService { final ids = trashedAssets.map((e) => e.remoteId!).toList(); await _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - ids, - state: AssetState.remote, - ); + await _assetRepository.deleteAllByRemoteId(ids, state: AssetState.remote); - final merged = await _assetRepository.getAllByRemoteId( - ids, - state: AssetState.merged, - ); + final merged = await _assetRepository.getAllByRemoteId(ids, state: AssetState.merged); if (merged.isEmpty) { return; } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index e8acf791a2..dca5c02feb 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -32,12 +32,7 @@ final uploadServiceProvider = Provider((ref) { }); class UploadService { - UploadService( - this._uploadRepository, - this._backupRepository, - this._storageRepository, - this._localAssetRepository, - ) { + UploadService(this._uploadRepository, this._backupRepository, this._storageRepository, this._localAssetRepository) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -114,10 +109,7 @@ class UploadService { /// Find backup candidates /// Build the upload tasks /// Enqueue the tasks - Future startBackup( - String userId, - void Function(EnqueueStatus status) onEnqueueTasks, - ) async { + Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { shouldAbortQueuingTasks = false; final candidates = await _backupRepository.getCandidates(userId); @@ -146,12 +138,7 @@ class UploadService { count += tasks.length; enqueueTasks(tasks); - onEnqueueTasks( - EnqueueStatus( - enqueueCount: count, - totalCount: candidates.length, - ), - ); + onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length)); } } } @@ -205,10 +192,7 @@ class UploadService { return; } - final uploadTask = await _getLivePhotoUploadTask( - localAsset, - response['id'] as String, - ); + final uploadTask = await _getLivePhotoUploadTask(localAsset, response['id'] as String); if (uploadTask == null) { return; @@ -220,11 +204,7 @@ class UploadService { } } - Future _getUploadTask( - LocalAsset asset, { - String group = kBackupGroup, - int? priority, - }) async { + Future _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -252,12 +232,7 @@ class UploadService { return null; } - final originalFileName = entity.isLivePhoto - ? p.setExtension( - asset.name, - p.extension(file.path), - ) - : asset.name; + final originalFileName = entity.isLivePhoto ? p.setExtension(asset.name, p.extension(file.path)) : asset.name; String metadata = UploadTaskMetadata( localAssetId: asset.id, @@ -275,10 +250,7 @@ class UploadService { ); } - Future _getLivePhotoUploadTask( - LocalAsset asset, - String livePhotoVideoId, - ) async { + Future _getLivePhotoUploadTask(LocalAsset asset, String livePhotoVideoId) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -289,9 +261,7 @@ class UploadService { return null; } - final fields = { - 'livePhotoVideoId': livePhotoVideoId, - }; + final fields = {'livePhotoVideoId': livePhotoVideoId}; return buildUploadTask( file, @@ -357,17 +327,9 @@ class UploadTaskMetadata { final bool isLivePhotos; final String livePhotoVideoId; - const UploadTaskMetadata({ - required this.localAssetId, - required this.isLivePhotos, - required this.livePhotoVideoId, - }); + const UploadTaskMetadata({required this.localAssetId, required this.isLivePhotos, required this.livePhotoVideoId}); - UploadTaskMetadata copyWith({ - String? localAssetId, - bool? isLivePhotos, - String? livePhotoVideoId, - }) { + UploadTaskMetadata copyWith({String? localAssetId, bool? isLivePhotos, String? livePhotoVideoId}) { return UploadTaskMetadata( localAssetId: localAssetId ?? this.localAssetId, isLivePhotos: isLivePhotos ?? this.isLivePhotos, diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index fb2022784f..8c5d21b389 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/repositories/widget.repository.dart'; final widgetServiceProvider = Provider((ref) { - return WidgetService( - ref.watch(widgetRepositoryProvider), - ); + return WidgetService(ref.watch(widgetRepositoryProvider)); }); class WidgetService { diff --git a/mobile/lib/theme/color_scheme.dart b/mobile/lib/theme/color_scheme.dart index c01b7cfa5a..f32b358ad9 100644 --- a/mobile/lib/theme/color_scheme.dart +++ b/mobile/lib/theme/color_scheme.dart @@ -6,10 +6,7 @@ final Map _themePresets = { ImmichColorPreset.indigo: ImmichTheme( light: ColorScheme.fromSeed( seedColor: immichBrandColorLight, - ).copyWith( - primary: immichBrandColorLight, - onSurface: const Color.fromARGB(255, 34, 31, 32), - ), + ).copyWith(primary: immichBrandColorLight, onSurface: const Color.fromARGB(255, 34, 31, 32)), dark: ColorScheme.fromSeed( seedColor: immichBrandColorDark, brightness: Brightness.dark, @@ -17,24 +14,15 @@ final Map _themePresets = { ), ImmichColorPreset.deepPurple: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF6F43C0)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3BBFF), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3BBFF), brightness: Brightness.dark), ), ImmichColorPreset.pink: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFED79B5), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5), brightness: Brightness.dark), ), ImmichColorPreset.red: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFC51C16)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3302F), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3302F), brightness: Brightness.dark), ), ImmichColorPreset.orange: ImmichTheme( light: ColorScheme.fromSeed( @@ -49,37 +37,22 @@ final Map _themePresets = { ), ImmichColorPreset.yellow: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFFFB400), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400), brightness: Brightness.dark), ), ImmichColorPreset.lime: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFCDDC39), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39), brightness: Brightness.dark), ), ImmichColorPreset.green: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF18C249), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249), brightness: Brightness.dark), ), ImmichColorPreset.cyan: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF00BCD4), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4), brightness: Brightness.dark), ), ImmichColorPreset.slateGray: ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: const Color(0xFF696969), - dynamicSchemeVariant: DynamicSchemeVariant.neutral, - ), + light: ColorScheme.fromSeed(seedColor: const Color(0xFF696969), dynamicSchemeVariant: DynamicSchemeVariant.neutral), dark: ColorScheme.fromSeed( seedColor: const Color(0xff696969), brightness: Brightness.dark, diff --git a/mobile/lib/theme/dynamic_theme.dart b/mobile/lib/theme/dynamic_theme.dart index 8ebf783469..99b949c9ac 100644 --- a/mobile/lib/theme/dynamic_theme.dart +++ b/mobile/lib/theme/dynamic_theme.dart @@ -18,14 +18,8 @@ abstract final class DynamicTheme { // Some palettes do not generate surface container colors accurately, // so we regenerate all colors using the primary color _theme = ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.light, - ), - dark: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.dark, - ), + light: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.light), + dark: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.dark), ); } } catch (error) { diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index 32695ef26e..8e3773839c 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -10,10 +10,7 @@ class ImmichTheme { const ImmichTheme({required this.light, required this.dark}); } -ThemeData getThemeData({ - required ColorScheme colorScheme, - required Locale locale, -}) { +ThemeData getThemeData({required ColorScheme colorScheme, required Locale locale}) { final isDark = colorScheme.brightness == Brightness.dark; return ThemeData( @@ -26,9 +23,7 @@ ThemeData getThemeData({ scaffoldBackgroundColor: colorScheme.surface, splashColor: colorScheme.primary.withValues(alpha: 0.1), highlightColor: colorScheme.primary.withValues(alpha: 0.1), - bottomSheetTheme: BottomSheetThemeData( - backgroundColor: colorScheme.surfaceContainer, - ), + bottomSheetTheme: BottomSheetThemeData(backgroundColor: colorScheme.surfaceContainer), fontFamily: _getFontFamilyFromLocale(locale), snackBarTheme: SnackBarThemeData( contentTextStyle: TextStyle( @@ -52,30 +47,12 @@ ThemeData getThemeData({ centerTitle: true, ), textTheme: const TextTheme( - displayLarge: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - displayMedium: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - displaySmall: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - titleSmall: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - ), - titleMedium: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w600, - ), - titleLarge: TextStyle( - fontSize: 26.0, - fontWeight: FontWeight.w600, - ), + displayLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + displayMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + displaySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + titleSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600), + titleMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600), + titleLarge: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w600), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( @@ -83,81 +60,43 @@ ThemeData getThemeData({ foregroundColor: isDark ? Colors.black87 : Colors.white, ), ), - chipTheme: const ChipThemeData( - side: BorderSide.none, - ), - sliderTheme: const SliderThemeData( - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), - trackHeight: 2.0, - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - type: BottomNavigationBarType.fixed, - ), + chipTheme: const ChipThemeData(side: BorderSide.none), + sliderTheme: const SliderThemeData(thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), trackHeight: 2.0), + bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed), popupMenuTheme: const PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, - labelTextStyle: const WidgetStatePropertyAll( - TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), + labelTextStyle: const WidgetStatePropertyAll(TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), + borderSide: BorderSide(color: colorScheme.primary), borderRadius: const BorderRadius.all(Radius.circular(15)), ), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), - ), - textSelectionTheme: TextSelectionThemeData( - cursorColor: colorScheme.primary, + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), + textSelectionTheme: TextSelectionThemeData(cursorColor: colorScheme.primary), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ), inputDecorationTheme: InputDecorationTheme( - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), - ), + focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: colorScheme.primary)), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), ), dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer), @@ -174,9 +113,7 @@ ThemeData getThemeData({ // This method replaces all surface shades in ImmichTheme to a static ones // as we are creating the colorscheme through seedColor the default surfaces are // tinted with primary color -ImmichTheme decolorizeSurfaces({ - required ImmichTheme theme, -}) { +ImmichTheme decolorizeSurfaces({required ImmichTheme theme}) { return ImmichTheme( light: theme.light.copyWith( surface: const Color(0xFFf9f9f9), diff --git a/mobile/lib/utils/backup_progress.dart b/mobile/lib/utils/backup_progress.dart index 3d2af2877e..36050f5e20 100644 --- a/mobile/lib/utils/backup_progress.dart +++ b/mobile/lib/utils/backup_progress.dart @@ -60,11 +60,7 @@ class ThrottleProgressUpdate { int progress = 0; int total = 0; - void call({ - final String? title, - final int progress = 0, - final int total = 0, - }) { + void call({final String? title, final int progress = 0, final int total = 0}) { final time = Timeline.now; this.title = title ?? this.title; this.progress = progress; diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 26f3b49242..8c4ca077c4 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -48,10 +48,7 @@ abstract final class Bootstrap { ); } - static Future initDomain( - Isar db, { - bool shouldBufferLogs = true, - }) async { + static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { await StoreService.init(storeRepository: IsarStoreRepository(db)); await LogService.init( logRepository: IsarLogRepository(db), diff --git a/mobile/lib/utils/cache/custom_image_cache.dart b/mobile/lib/utils/cache/custom_image_cache.dart index 6465a68222..194c55f9df 100644 --- a/mobile/lib/utils/cache/custom_image_cache.dart +++ b/mobile/lib/utils/cache/custom_image_cache.dart @@ -39,7 +39,8 @@ final class CustomImageCache implements ImageCache { /// Gets the cache for the given key /// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] /// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider] - ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider || + ImageCache _cacheForKey(Object key) => + (key is ImmichLocalImageProvider || key is ImmichRemoteImageProvider || key is LocalFullImageProvider || key is RemoteFullImageProvider) @@ -73,8 +74,7 @@ final class CustomImageCache implements ImageCache { Object key, ImageStreamCompleter Function() loader, { ImageErrorListener? onError, - }) => - _cacheForKey(key).putIfAbsent(key, loader, onError: onError); + }) => _cacheForKey(key).putIfAbsent(key, loader, onError: onError); @override ImageCacheStatus statusForKey(Object key) => _cacheForKey(key).statusForKey(key); diff --git a/mobile/lib/utils/color_filter_generator.dart b/mobile/lib/utils/color_filter_generator.dart index d4217a9319..92aed4b1a0 100644 --- a/mobile/lib/utils/color_filter_generator.dart +++ b/mobile/lib/utils/color_filter_generator.dart @@ -27,9 +27,7 @@ class BrightnessFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.brightnessAdjustMatrix(brightness), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.brightnessAdjustMatrix(brightness)), child: child, ); } @@ -44,9 +42,7 @@ class SaturationFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.saturationAdjustMatrix(saturation), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.saturationAdjustMatrix(saturation)), child: child, ); } diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index e8d3e6fb24..5451b569ca 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -68,21 +68,10 @@ Debouncer useDebouncer({ Duration interval = const Duration(milliseconds: 300), Duration? maxWaitTime, List? keys, -}) => - use( - _DebouncerHook( - interval: interval, - maxWaitTime: maxWaitTime, - keys: keys, - ), - ); +}) => use(_DebouncerHook(interval: interval, maxWaitTime: maxWaitTime, keys: keys)); class _DebouncerHook extends Hook { - const _DebouncerHook({ - required this.interval, - this.maxWaitTime, - super.keys, - }); + const _DebouncerHook({required this.interval, this.maxWaitTime, super.keys}); final Duration interval; final Duration? maxWaitTime; @@ -92,10 +81,7 @@ class _DebouncerHook extends Hook { } class _DebouncerHookState extends HookState { - late final debouncer = Debouncer( - interval: hook.interval, - maxWaitTime: hook.maxWaitTime, - ); + late final debouncer = Debouncer(interval: hook.interval, maxWaitTime: hook.maxWaitTime); @override Debouncer build(_) => debouncer; diff --git a/mobile/lib/utils/draggable_scroll_controller.dart b/mobile/lib/utils/draggable_scroll_controller.dart index cd7ae5b0e1..bab5214446 100644 --- a/mobile/lib/utils/draggable_scroll_controller.dart +++ b/mobile/lib/utils/draggable_scroll_controller.dart @@ -5,20 +5,12 @@ import 'package:flutter_hooks/flutter_hooks.dart'; /// /// See also: /// - [DraggableScrollableController] -DraggableScrollableController useDraggableScrollController({ - List? keys, -}) { - return use( - _DraggableScrollControllerHook( - keys: keys, - ), - ); +DraggableScrollableController useDraggableScrollController({List? keys}) { + return use(_DraggableScrollControllerHook(keys: keys)); } class _DraggableScrollControllerHook extends Hook { - const _DraggableScrollControllerHook({ - super.keys, - }); + const _DraggableScrollControllerHook({super.keys}); @override HookState> createState() => diff --git a/mobile/lib/utils/hooks/app_settings_update_hook.dart b/mobile/lib/utils/hooks/app_settings_update_hook.dart index a4968feeae..954e44229a 100644 --- a/mobile/lib/utils/hooks/app_settings_update_hook.dart +++ b/mobile/lib/utils/hooks/app_settings_update_hook.dart @@ -3,16 +3,11 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -ValueNotifier useAppSettingsState( - AppSettingsEnum key, -) { +ValueNotifier useAppSettingsState(AppSettingsEnum key) { final notifier = useState(Store.get(key.storeKey, key.defaultValue)); // Listen to changes to the notifier and update app settings - useValueChanged( - notifier.value, - (_, __) => Store.put(key.storeKey, notifier.value), - ); + useValueChanged(notifier.value, (_, __) => Store.put(key.storeKey, notifier.value)); return notifier; } diff --git a/mobile/lib/utils/hooks/blurhash_hook.dart b/mobile/lib/utils/hooks/blurhash_hook.dart index 62208c4cf5..ac5fd31724 100644 --- a/mobile/lib/utils/hooks/blurhash_hook.dart +++ b/mobile/lib/utils/hooks/blurhash_hook.dart @@ -10,9 +10,7 @@ ObjectRef useBlurHashRef(Asset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbhash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbhash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } @@ -22,9 +20,7 @@ ObjectRef useDriftBlurHashRef(RemoteAsset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbHash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbHash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart index 04bc978754..663bca3dbf 100644 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ b/mobile/lib/utils/hooks/crop_controller_hook.dart @@ -4,9 +4,5 @@ import 'dart:ui'; // Import the dart:ui library for Rect /// A hook that provides a [CropController] instance. CropController useCropController() { - return useMemoized( - () => CropController( - defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), - ), - ); + return useMemoized(() => CropController(defaultCrop: const Rect.fromLTRB(0, 0, 1, 1))); } diff --git a/mobile/lib/utils/hooks/interval_hook.dart b/mobile/lib/utils/hooks/interval_hook.dart index 0c346065f7..907fbad102 100644 --- a/mobile/lib/utils/hooks/interval_hook.dart +++ b/mobile/lib/utils/hooks/interval_hook.dart @@ -8,11 +8,8 @@ void useInterval(Duration delay, VoidCallback callback) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer.periodic(delay, (_) => savedCallback.value()); - return timer.cancel; - }, - [delay], - ); + useEffect(() { + final timer = Timer.periodic(delay, (_) => savedCallback.value()); + return timer.cancel; + }, [delay]); } diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart index 577e46f5d4..36b78d8631 100644 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ b/mobile/lib/utils/hooks/timer_hook.dart @@ -2,26 +2,15 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -RestartableTimer useTimer( - Duration duration, - void Function() callback, -) { - return use( - _TimerHook( - duration: duration, - callback: callback, - ), - ); +RestartableTimer useTimer(Duration duration, void Function() callback) { + return use(_TimerHook(duration: duration, callback: callback)); } class _TimerHook extends Hook { final Duration duration; final void Function() callback; - const _TimerHook({ - required this.duration, - required this.callback, - }); + const _TimerHook({required this.duration, required this.callback}); @override HookState> createState() => _TimerHookState(); } diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index f64757cf9d..a4c97a532f 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -10,11 +10,7 @@ class HttpSSLCertOverride extends HttpOverrides { final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride( - this._allowSelfSignedSSLCert, - this._serverHost, - this._clientCert, - ) { + HttpSSLCertOverride(this._allowSelfSignedSSLCert, this._serverHost, this._clientCert) { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { diff --git a/mobile/lib/utils/http_ssl_options.dart b/mobile/lib/utils/http_ssl_options.dart index eaf6e77e4a..c4e2ad69f7 100644 --- a/mobile/lib/utils/http_ssl_options.dart +++ b/mobile/lib/utils/http_ssl_options.dart @@ -31,15 +31,12 @@ class HttpSSLOptions { HttpOverrides.global = HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); if (applyNative && Platform.isAndroid) { - _channel.invokeMethod("apply", [ - allowSelfSignedSSLCert, - serverHost, - clientCert?.data, - clientCert?.password, - ]).onError((e, _) { - final log = Logger("HttpSSLOptions"); - log.severe('Failed to set SSL options', e.message); - }); + _channel + .invokeMethod("apply", [allowSelfSignedSSLCert, serverHost, clientCert?.data, clientCert?.password]) + .onError((e, _) { + final log = Logger("HttpSSLOptions"); + log.severe('Failed to set SSL options', e.message); + }); } } } diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index bde50f3a90..21722cb901 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -5,24 +5,15 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:openapi/api.dart'; -String getThumbnailUrl( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrl(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailUrlForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKey( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKey(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailCacheKeyForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKeyForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKeyForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (type == AssetMediaSize.thumbnail) { return 'thumbnail-image-$id'; } else { @@ -30,30 +21,18 @@ String getThumbnailCacheKeyForRemoteId( } } -String getAlbumThumbnailUrl( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbnailUrl(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailUrlForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailUrlForRemoteId(album.thumbnail.value!.remoteId!, type: type); } -String getAlbumThumbNailCacheKey( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbNailCacheKey(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailCacheKeyForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailCacheKeyForRemoteId(album.thumbnail.value!.remoteId!, type: type); } String getOriginalUrlForRemoteId(final String id) { @@ -66,10 +45,7 @@ String getImageCacheKey(final Asset asset) { return '${isFromDto ? asset.remoteId : asset.id}_fullStage'; } -String getThumbnailUrlForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } diff --git a/mobile/lib/utils/immich_loading_overlay.dart b/mobile/lib/utils/immich_loading_overlay.dart index b44f78e0bd..be49c3bae9 100644 --- a/mobile/lib/utils/immich_loading_overlay.dart +++ b/mobile/lib/utils/immich_loading_overlay.dart @@ -9,10 +9,7 @@ final _loadingEntry = OverlayEntry( child: DecoratedBox( decoration: BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), child: const Center( - child: DelayedLoadingIndicator( - delay: Duration(seconds: 1), - fadeInDuration: Duration(milliseconds: 400), - ), + child: DelayedLoadingIndicator(delay: Duration(seconds: 1), fadeInDuration: Duration(milliseconds: 400)), ), ), ), diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 7f8e8510d3..a57c3ebbd5 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -51,15 +51,9 @@ Cancelable runInIsolateGentle({ HttpSSLOptions.apply(applyNative: false); return await computation(ref); } on CanceledError { - log.warning( - "Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}", - ); + log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}"); } catch (error, stack) { - log.severe( - "Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", - error, - stack, - ); + log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { await LogService.I.flushBuffer(); diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 3dd849a044..80e20b7c6c 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -48,21 +48,18 @@ class MapUtils { ); static Map _addFeature(MapMarker marker) => { - 'type': 'Feature', - 'id': marker.assetRemoteId, - 'geometry': { - 'type': 'Point', - 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], - }, - }; + 'type': 'Feature', + 'id': marker.assetRemoteId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], + }, + }; - static Map generateGeoJsonForMarkers( - List markers, - ) => - { - 'type': 'FeatureCollection', - 'features': markers.map(_addFeature).toList(), - }; + static Map generateGeoJsonForMarkers(List markers) => { + 'type': 'FeatureCollection', + 'features': markers.map(_addFeature).toList(), + }; static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ required BuildContext context, @@ -71,10 +68,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog( - context: context, - builder: (context) => _LocationServiceDisabledDialog(), - ); + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); return (null, LocationPermission.deniedForever); } @@ -116,24 +110,24 @@ class MapUtils { class _LocationServiceDisabledDialog extends ConfirmDialog { _LocationServiceDisabledDialog() - : super( - title: 'map_location_service_disabled_title'.tr(), - content: 'map_location_service_disabled_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () async { - await Geolocator.openLocationSettings(); - }, - ); + : super( + title: 'map_location_service_disabled_title'.tr(), + content: 'map_location_service_disabled_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); } class _LocationPermissionDisabledDialog extends ConfirmDialog { _LocationPermissionDisabledDialog() - : super( - title: 'map_no_location_permission_title'.tr(), - content: 'map_no_location_permission_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () {}, - ); + : super( + title: 'map_no_location_permission_title'.tr(), + content: 'map_no_location_permission_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () {}, + ); } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 7b61e5521d..609b3cfca8 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -85,16 +85,14 @@ Future _migrateTo(Isar db, int version) async { Future _migrateDeviceAsset(Isar db) async { final ids = Platform.isAndroid ? (await db.androidDeviceAssets.where().findAll()) - .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) - .toList() + .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) + .toList() : (await db.iOSDeviceAssets.where().findAll()).map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)).toList(); final PermissionState ps = await PhotoManager.requestPermissionExtend(); if (!ps.hasAccess) { if (kDebugMode) { - debugPrint( - "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", - ); + debugPrint("[MIGRATION] Photo library permission not granted. Skipping device asset migration."); } return; @@ -105,9 +103,7 @@ Future _migrateDeviceAsset(Isar db) async { if (paths.isEmpty) { localAssets = (await db.assets.where().anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)).findAll()) - .map( - (a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt), - ) + .map((a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt)) .toList(); } else { final AssetPathEntity albumWithAll = paths.first; @@ -129,34 +125,24 @@ Future _migrateDeviceAsset(Isar db) async { compare: (a, b) => a.assetId.compareTo(b.assetId), both: (deviceAsset, asset) { toAdd.add( - DeviceAssetEntity( - assetId: deviceAsset.assetId, - hash: deviceAsset.hash!, - modifiedTime: asset.dateTime!, - ), + DeviceAssetEntity(assetId: deviceAsset.assetId, hash: deviceAsset.hash!, modifiedTime: asset.dateTime!), ); return false; }, onlyFirst: (deviceAsset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}'); } }, onlySecond: (asset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}'); } }, ); if (kDebugMode) { - debugPrint( - "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", - ); + debugPrint("[MIGRATION] Total number of device assets migrated - ${toAdd.length}"); } await db.writeTxn(() async { @@ -171,24 +157,17 @@ Future migrateDeviceAssetToSqlite(Isar db, Drift drift) async { for (final deviceAsset in isarDeviceAssets) { batch.update( drift.localAssetEntity, - LocalAssetEntityCompanion( - checksum: Value(base64.encode(deviceAsset.hash)), - ), + LocalAssetEntityCompanion(checksum: Value(base64.encode(deviceAsset.hash))), where: (t) => t.id.equals(deviceAsset.assetId), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating device assets to SQLite: $error", - ); + debugPrint("[MIGRATION] Error while migrating device assets to SQLite: $error"); } } -Future migrateBackupAlbumsToSqlite( - Isar db, - Drift drift, -) async { +Future migrateBackupAlbumsToSqlite(Isar db, Drift drift) async { try { final isarBackupAlbums = await db.backupAlbums.where().findAll(); // Recents is a virtual album on Android, and we don't have it with the new sync @@ -197,23 +176,17 @@ Future migrateBackupAlbumsToSqlite( final recentAlbum = isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); if (recentAlbum != null) { await drift.localAlbumEntity.update().write( - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.selected), - ), - ); + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.selected)), + ); final excluded = isarBackupAlbums - .where( - (album) => album.selection == isar_backup_album.BackupSelection.exclude, - ) + .where((album) => album.selection == isar_backup_album.BackupSelection.exclude) .map((album) => album.id) .toList(); await drift.batch((batch) async { for (final id in excluded) { batch.update( drift.localAlbumEntity, - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.excluded), - ), + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.excluded)), where: (t) => t.id.equals(id), ); } @@ -227,22 +200,18 @@ Future migrateBackupAlbumsToSqlite( batch.update( drift.localAlbumEntity, LocalAlbumEntityCompanion( - backupSelection: Value( - switch (album.selection) { - isar_backup_album.BackupSelection.none => BackupSelection.none, - isar_backup_album.BackupSelection.select => BackupSelection.selected, - isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, - }, - ), + backupSelection: Value(switch (album.selection) { + isar_backup_album.BackupSelection.none => BackupSelection.none, + isar_backup_album.BackupSelection.select => BackupSelection.selected, + isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, + }), ), where: (t) => t.id.equals(album.id), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating backup albums to SQLite: $error", - ); + debugPrint("[MIGRATION] Error while migrating backup albums to SQLite: $error"); } } @@ -259,12 +228,10 @@ Future runNewSync(WidgetRef ref, {bool full = false}) async { final backgroundManager = ref.read(backgroundSyncProvider); Future.wait([ - backgroundManager.syncLocal(full: full).then( - (_) { - Logger("runNewSync").fine("Hashing assets after syncLocal"); - backgroundManager.hashAssets(); - }, - ), + backgroundManager.syncLocal(full: full).then((_) { + Logger("runNewSync").fine("Hashing assets after syncLocal"); + backgroundManager.hashAssets(); + }), backgroundManager.syncRemote(), ]); } diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 8e14d232f8..efbcf1c139 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -17,16 +17,8 @@ dynamic upgradeDto(dynamic value, String targetType) { 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, 'mapLightStyleUrl', 'https://tiles.immich.cloud/v1/style/light.json'); + addDefault(value, 'mapDarkStyleUrl', 'https://tiles.immich.cloud/v1/style/dark.json'); } case 'UserResponseDto': if (value is Map) { diff --git a/mobile/lib/utils/remote_album.utils.dart b/mobile/lib/utils/remote_album.utils.dart index af853e08d2..b63df899ce 100644 --- a/mobile/lib/utils/remote_album.utils.dart +++ b/mobile/lib/utils/remote_album.utils.dart @@ -1,55 +1,37 @@ import 'package:collection/collection.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -typedef AlbumSortFn = List Function( - List albums, - bool isReverse, -); +typedef AlbumSortFn = List Function(List albums, bool isReverse); class _RemoteAlbumSortHandlers { const _RemoteAlbumSortHandlers._(); static const AlbumSortFn created = _sortByCreated; - static List _sortByCreated( - List albums, - bool isReverse, - ) { + static List _sortByCreated(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.createdAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn title = _sortByTitle; - static List _sortByTitle( - List albums, - bool isReverse, - ) { + static List _sortByTitle(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.name); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn lastModified = _sortByLastModified; - static List _sortByLastModified( - List albums, - bool isReverse, - ) { + static List _sortByLastModified(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.updatedAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn assetCount = _sortByAssetCount; - static List _sortByAssetCount( - List albums, - bool isReverse, - ) { + static List _sortByAssetCount(List albums, bool isReverse) { final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn mostRecent = _sortByMostRecent; - static List _sortByMostRecent( - List albums, - bool isReverse, - ) { + static List _sortByMostRecent(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For most recent, we sort by updatedAt in descending order return b.updatedAt.compareTo(a.updatedAt); @@ -58,10 +40,7 @@ class _RemoteAlbumSortHandlers { } static const AlbumSortFn mostOldest = _sortByMostOldest; - static List _sortByMostOldest( - List albums, - bool isReverse, - ) { + static List _sortByMostOldest(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For oldest, we sort by createdAt in ascending order return a.createdAt.compareTo(b.createdAt); @@ -72,14 +51,8 @@ class _RemoteAlbumSortHandlers { enum RemoteAlbumSortMode { title("library_page_sort_title", _RemoteAlbumSortHandlers.title), - assetCount( - "library_page_sort_asset_count", - _RemoteAlbumSortHandlers.assetCount, - ), - lastModified( - "library_page_sort_last_modified", - _RemoteAlbumSortHandlers.lastModified, - ), + assetCount("library_page_sort_asset_count", _RemoteAlbumSortHandlers.assetCount), + lastModified("library_page_sort_last_modified", _RemoteAlbumSortHandlers.lastModified), created("library_page_sort_created", _RemoteAlbumSortHandlers.created), mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent), mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest); diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index 633ce2463a..d128ef8fac 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -16,27 +16,21 @@ import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -void handleShareAssets( - WidgetRef ref, - BuildContext context, - Iterable selection, -) { +void handleShareAssets(WidgetRef ref, BuildContext context, Iterable selection) { showDialog( context: context, builder: (BuildContext buildContext) { - ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -58,11 +52,7 @@ Future handleArchiveAssets( ? 'moved_to_archive'.t(context: context, args: {'count': selection.length}) : 'moved_to_library'.t(context: context, args: {'count': selection.length}); if (context.mounted) { - ImmichToast.show( - context: context, - msg: message, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: message, gravity: toastGravity); } } } @@ -83,20 +73,12 @@ Future handleFavoriteAssets( ? 'Added ${selection.length} $assetOrAssets to favorites' : 'Removed ${selection.length} $assetOrAssets from favorites'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: toastGravity); } } } -Future handleEditDateTime( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditDateTime(WidgetRef ref, BuildContext context, List selection) async { DateTime? initialDate; String? timeZone; Duration? offset; @@ -122,27 +104,17 @@ Future handleEditDateTime( ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); } -Future handleEditLocation( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditLocation(WidgetRef ref, BuildContext context, List selection) async { LatLng? initialLatLng; if (selection.length == 1) { final asset = selection.first; final assetWithExif = await ref.watch(assetServiceProvider).loadExif(asset); if (assetWithExif.exifInfo?.latitude != null && assetWithExif.exifInfo?.longitude != null) { - initialLatLng = LatLng( - assetWithExif.exifInfo!.latitude!, - assetWithExif.exifInfo!.longitude!, - ); + initialLatLng = LatLng(assetWithExif.exifInfo!.latitude!, assetWithExif.exifInfo!.longitude!); } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return; @@ -165,11 +137,7 @@ Future handleSetAssetsVisibility( ? 'Added ${selection.length} $assetOrAssets to locked folder' : 'Removed ${selection.length} $assetOrAssets from locked folder'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: ToastGravity.BOTTOM); } } } diff --git a/mobile/lib/utils/throttle.dart b/mobile/lib/utils/throttle.dart index c4427472d3..8b41d92318 100644 --- a/mobile/lib/utils/throttle.dart +++ b/mobile/lib/utils/throttle.dart @@ -25,17 +25,11 @@ class Throttler { /// Creates a [Throttler] that will be disposed automatically. If no [interval] is provided, a /// default interval of 300ms is used to throttle the function calls -Throttler useThrottler({ - Duration interval = const Duration(milliseconds: 300), - List? keys, -}) => +Throttler useThrottler({Duration interval = const Duration(milliseconds: 300), List? keys}) => use(_ThrottleHook(interval: interval, keys: keys)); class _ThrottleHook extends Hook { - const _ThrottleHook({ - required this.interval, - super.keys, - }); + const _ThrottleHook({required this.interval, super.keys}); final Duration interval; diff --git a/mobile/lib/utils/thumbnail_utils.dart b/mobile/lib/utils/thumbnail_utils.dart index 758305c8bc..685dc2b1c2 100644 --- a/mobile/lib/utils/thumbnail_utils.dart +++ b/mobile/lib/utils/thumbnail_utils.dart @@ -3,12 +3,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -String getAltText( - ExifInfo? exifInfo, - DateTime fileCreatedAt, - AssetType type, - List peopleNames, -) { +String getAltText(ExifInfo? exifInfo, DateTime fileCreatedAt, AssetType type, List peopleNames) { if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) { return exifInfo.description!; } @@ -41,14 +36,14 @@ String getAltText( 1 => "image_alt_text_date_place_1_person", 2 => "image_alt_text_date_place_2_people", 3 => "image_alt_text_date_place_3_people", - _ => "image_alt_text_date_place_4_or_more_people" + _ => "image_alt_text_date_place_4_or_more_people", }) : (switch (peopleNames.length) { 0 => "image_alt_text_date", 1 => "image_alt_text_date_1_person", 2 => "image_alt_text_date_2_people", 3 => "image_alt_text_date_3_people", - _ => "image_alt_text_date_4_or_more_people" + _ => "image_alt_text_date_4_or_more_people", }); return (template, args); } diff --git a/mobile/lib/utils/url_helper.dart b/mobile/lib/utils/url_helper.dart index ec65b4f7ee..e3d5b8ed57 100644 --- a/mobile/lib/utils/url_helper.dart +++ b/mobile/lib/utils/url_helper.dart @@ -44,13 +44,14 @@ String punycodeEncodeUrl(String serverUrl) { final serverUri = Uri.tryParse(serverUrl); if (serverUri == null || serverUri.host.isEmpty) return ''; - final encodedHost = Uri.decodeComponent(serverUri.host).split('.').map( - (segment) { - // If segment is already ASCII, then return as it is. - if (segment.runes.every((c) => c < 0x80)) return segment; - return 'xn--${punycodeEncode(segment)}'; - }, - ).join('.'); + final encodedHost = Uri.decodeComponent(serverUri.host) + .split('.') + .map((segment) { + // If segment is already ASCII, then return as it is. + if (segment.runes.every((c) => c < 0x80)) return segment; + return 'xn--${punycodeEncode(segment)}'; + }) + .join('.'); return serverUri.replace(host: encodedHost).toString(); } @@ -76,15 +77,16 @@ String? punycodeDecodeUrl(String? serverUrl) { final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null; if (serverUri == null || serverUri.host.isEmpty) return null; - final decodedHost = serverUri.host.split('.').map( - (segment) { - if (segment.toLowerCase().startsWith('xn--')) { - return punycodeDecode(segment.substring(4)); - } - // If segment is not punycode encoded, then return as it is. - return segment; - }, - ).join('.'); + final decodedHost = serverUri.host + .split('.') + .map((segment) { + if (segment.toLowerCase().startsWith('xn--')) { + return punycodeDecode(segment.substring(4)); + } + // If segment is not punycode encoded, then return as it is. + return segment; + }) + .join('.'); return Uri.decodeFull(serverUri.replace(host: decodedHost).toString()); } diff --git a/mobile/lib/utils/version_compatibility.dart b/mobile/lib/utils/version_compatibility.dart index 19d9aa38d4..fa8dfb0b9e 100644 --- a/mobile/lib/utils/version_compatibility.dart +++ b/mobile/lib/utils/version_compatibility.dart @@ -1,9 +1,4 @@ -String? getVersionCompatibilityMessage( - int appMajor, - int appMinor, - int serverMajor, - int serverMinor, -) { +String? getVersionCompatibilityMessage(int appMajor, int appMinor, int serverMajor, int serverMinor) { if (serverMajor != appMajor) { return 'Your app major version is not compatible with the server!'; } diff --git a/mobile/lib/widgets/activities/activity_text_field.dart b/mobile/lib/widgets/activities/activity_text_field.dart index f111de5e53..e3958b6287 100644 --- a/mobile/lib/widgets/activities/activity_text_field.dart +++ b/mobile/lib/widgets/activities/activity_text_field.dart @@ -13,12 +13,7 @@ class ActivityTextField extends HookConsumerWidget { final String? likeId; final Function(String) onSubmit; - const ActivityTextField({ - required this.onSubmit, - this.isEnabled = true, - this.likeId, - super.key, - }); + const ActivityTextField({required this.onSubmit, this.isEnabled = true, this.likeId, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,13 +26,10 @@ class ActivityTextField extends HookConsumerWidget { final liked = likeId != null; // Show keyboard immediately on activities open - useEffect( - () { - inputFocusNode.requestFocus(); - return null; - }, - [], - ); + useEffect(() { + inputFocusNode.requestFocus(); + return null; + }, []); // Pass text to callback and reset controller void onEditingComplete() { @@ -70,29 +62,19 @@ class ActivityTextField extends HookConsumerWidget { prefixIcon: user != null ? Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: UserCircleAvatar( - user: user, - size: 30, - radius: 15, - ), + child: UserCircleAvatar(user: user, size: 30, radius: 15), ) : null, suffixIcon: Padding( padding: const EdgeInsets.only(right: 10), child: IconButton( - icon: Icon( - liked ? Icons.favorite_rounded : Icons.favorite_border_rounded, - ), + icon: Icon(liked ? Icons.favorite_rounded : Icons.favorite_border_rounded), onPressed: liked ? removeLike : addLike, ), ), suffixIconColor: liked ? Colors.red[700] : null, hintText: !isEnabled ? 'shared_album_activities_input_disable'.tr() : 'say_something'.tr(), - hintStyle: TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - color: Colors.grey[600], - ), + hintStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 14, color: Colors.grey[600]), ), onEditingComplete: onEditingComplete, onTapOutside: (_) => inputFocusNode.unfocus(), diff --git a/mobile/lib/widgets/activities/activity_tile.dart b/mobile/lib/widgets/activities/activity_tile.dart index a2bc5135c6..4b66bd5eaf 100644 --- a/mobile/lib/widgets/activities/activity_tile.dart +++ b/mobile/lib/widgets/activities/activity_tile.dart @@ -26,10 +26,7 @@ class ActivityTile extends HookConsumerWidget { ? Container( width: 44, alignment: Alignment.center, - child: Icon( - Icons.favorite_rounded, - color: Colors.red[700], - ), + child: Icon(Icons.favorite_rounded, color: Colors.red[700]), ) : UserCircleAvatar(user: activity.user), title: _ActivityTitle( @@ -50,11 +47,7 @@ class _ActivityTitle extends StatelessWidget { final String createdAt; final bool leftAlign; - const _ActivityTitle({ - required this.userName, - required this.createdAt, - required this.leftAlign, - }); + const _ActivityTitle({required this.userName, required this.createdAt, required this.leftAlign}); @override Widget build(BuildContext context) { @@ -65,16 +58,8 @@ class _ActivityTitle extends StatelessWidget { mainAxisAlignment: leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max, children: [ - Text( - userName, - style: textStyle, - overflow: TextOverflow.ellipsis, - ), - if (leftAlign) - Text( - " • ", - style: textStyle, - ), + Text(userName, style: textStyle, overflow: TextOverflow.ellipsis), + if (leftAlign) Text(" • ", style: textStyle), Expanded( child: Text( createdAt, @@ -101,9 +86,7 @@ class _ActivityAssetThumbnail extends StatelessWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(4)), image: DecorationImage( - image: ImmichRemoteThumbnailProvider( - assetId: assetId, - ), + image: ImmichRemoteThumbnailProvider(assetId: assetId), fit: BoxFit.cover, ), ), diff --git a/mobile/lib/widgets/activities/dismissible_activity.dart b/mobile/lib/widgets/activities/dismissible_activity.dart index b6c083f616..2f017d51ed 100644 --- a/mobile/lib/widgets/activities/dismissible_activity.dart +++ b/mobile/lib/widgets/activities/dismissible_activity.dart @@ -8,20 +8,13 @@ class DismissibleActivity extends StatelessWidget { final ActivityTile body; final Function(String)? onDismiss; - const DismissibleActivity( - this.activityId, - this.body, { - this.onDismiss, - super.key, - }); + const DismissibleActivity(this.activityId, this.body, {this.onDismiss, super.key}); @override Widget build(BuildContext context) { return Dismissible( key: Key(activityId), - dismissThresholds: const { - DismissDirection.horizontal: 0.7, - }, + dismissThresholds: const {DismissDirection.horizontal: 0.7}, direction: DismissDirection.horizontal, confirmDismiss: (direction) => onDismiss != null ? showDialog( @@ -51,10 +44,7 @@ class _DismissBackground extends StatelessWidget { final AlignmentDirectional alignment; final bool withDeleteIcon; - const _DismissBackground({ - required this.withDeleteIcon, - this.alignment = AlignmentDirectional.centerStart, - }); + const _DismissBackground({required this.withDeleteIcon, this.alignment = AlignmentDirectional.centerStart}); @override Widget build(BuildContext context) { @@ -64,10 +54,7 @@ class _DismissBackground extends StatelessWidget { child: withDeleteIcon ? const Padding( padding: EdgeInsets.all(15), - child: Icon( - Icons.delete_sweep_rounded, - color: Colors.black, - ), + child: Icon(Icons.delete_sweep_rounded, color: Colors.black), ) : null, ); diff --git a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart index c256c558d6..d8f6a8885a 100644 --- a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart +++ b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart @@ -17,46 +17,33 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { /// The asset to add to an album final List assets; - const AddToAlbumBottomSheet({ - super.key, - required this.assets, - }); + const AddToAlbumBottomSheet({super.key, required this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); final albumService = ref.watch(albumServiceProvider); - useEffect( - () { - // Fetch album updates, e.g., cover image - ref.read(albumProvider.notifier).refreshRemoteAlbums(); + useEffect(() { + // Fetch album updates, e.g., cover image + ref.read(albumProvider.notifier).refreshRemoteAlbums(); - return null; - }, - [], - ); + return null; + }, []); void addToAlbum(Album album) async { - final result = await albumService.addAssets( - album, - assets, - ); + final result = await albumService.addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } } @@ -66,10 +53,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { return Card( elevation: 0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)), ), child: CustomScrollView( slivers: [ @@ -80,33 +64,17 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), - const Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), + const Align(alignment: Alignment.center, child: CustomDraggingHandle()), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'add_to_album'.tr(), - style: context.textTheme.displayMedium, - ), + Text('add_to_album'.tr(), style: context.textTheme.displayMedium), TextButton.icon( - icon: Icon( - Icons.add, - color: context.primaryColor, - ), - label: Text( - 'common_create_new_album'.tr(), - style: TextStyle(color: context.primaryColor), - ), + icon: Icon(Icons.add, color: context.primaryColor), + label: Text('common_create_new_album'.tr(), style: TextStyle(color: context.primaryColor)), onPressed: () { - context.pushRoute( - CreateAlbumRoute( - assets: assets, - ), - ); + context.pushRoute(CreateAlbumRoute(assets: assets)); }, ), ], diff --git a/mobile/lib/widgets/album/add_to_album_sliverlist.dart b/mobile/lib/widgets/album/add_to_album_sliverlist.dart index b0f2a0a49a..defbd90388 100644 --- a/mobile/lib/widgets/album/add_to_album_sliverlist.dart +++ b/mobile/lib/widgets/album/add_to_album_sliverlist.dart @@ -28,8 +28,10 @@ class AddToAlbumSliverList extends HookConsumerWidget { final sortedSharedAlbums = albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); return SliverList( - delegate: - SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), (context, index) { + delegate: SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), ( + context, + index, + ) { // Build shared expander if (index == 0 && sortedSharedAlbums.isNotEmpty) { return Padding( @@ -56,10 +58,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { // Build albums list final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0); final album = sortedAlbums[offset]; - return AlbumThumbnailListTile( - album: album, - onTap: enabled ? () => onAddToAlbum(album) : () {}, - ); + return AlbumThumbnailListTile(album: album, onTap: enabled ? () => onAddToAlbum(album) : () {}); }), ); } diff --git a/mobile/lib/widgets/album/album_action_filled_button.dart b/mobile/lib/widgets/album/album_action_filled_button.dart index 48a8a27f59..04447ffab6 100644 --- a/mobile/lib/widgets/album/album_action_filled_button.dart +++ b/mobile/lib/widgets/album/album_action_filled_button.dart @@ -6,12 +6,7 @@ class AlbumActionFilledButton extends StatelessWidget { final String labelText; final IconData iconData; - const AlbumActionFilledButton({ - super.key, - this.onPressed, - required this.labelText, - required this.iconData, - }); + const AlbumActionFilledButton({super.key, this.onPressed, required this.labelText, required this.iconData}); @override Widget build(BuildContext context) { @@ -20,24 +15,12 @@ class AlbumActionFilledButton extends StatelessWidget { child: OutlinedButton.icon( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), backgroundColor: context.colorScheme.surfaceContainerHigh, ), - icon: Icon( - iconData, - size: 18, - color: context.primaryColor, - ), - label: Text( - labelText, - style: context.textTheme.labelLarge?.copyWith(), - ), + icon: Icon(iconData, size: 18, color: context.primaryColor), + label: Text(labelText, style: context.textTheme.labelLarge?.copyWith()), onPressed: onPressed, ), ); diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index e8d0425d4e..6c56f5d843 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -16,13 +16,7 @@ class AlbumThumbnailCard extends ConsumerWidget { final bool showOwner; final bool showTitle; - const AlbumThumbnailCard({ - super.key, - required this.album, - this.onTap, - this.showOwner = false, - this.showTitle = true, - }); + const AlbumThumbnailCard({super.key, required this.album, this.onTap, this.showOwner = false, this.showTitle = true}); final Album album; @@ -36,24 +30,14 @@ class AlbumThumbnailCard extends ConsumerWidget { return Container( height: cardSize, width: cardSize, - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainerHigh, - ), + decoration: BoxDecoration(color: context.colorScheme.surfaceContainerHigh), child: Center( - child: Icon( - Icons.no_photography, - size: cardSize * .15, - color: context.colorScheme.primary, - ), + child: Icon(Icons.no_photography, size: cardSize * .15, color: context.colorScheme.primary), ), ); } - buildAlbumThumbnail() => ImmichThumbnail( - asset: album.thumbnail.value, - width: cardSize, - height: cardSize, - ); + buildAlbumThumbnail() => ImmichThumbnail(asset: album.thumbnail.value, width: cardSize, height: cardSize); buildAlbumTextRow() { // Add the owner name to the subtitle @@ -62,12 +46,7 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName!, - }, - ); + owner = 'shared_by_user'.t(context: context, args: {'user': album.ownerName!}); } } @@ -75,19 +54,12 @@ class AlbumThumbnailCard extends ConsumerWidget { TextSpan( children: [ TextSpan( - text: 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), + text: 'items_count'.t(context: context, args: {'count': album.assetCount}), ), if (owner != null) const TextSpan(text: ' • '), if (owner != null) TextSpan(text: owner), ], - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), overflow: TextOverflow.fade, ); @@ -106,9 +78,7 @@ class AlbumThumbnailCard extends ConsumerWidget { width: cardSize, height: cardSize, child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), ), diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index 8332cde889..423410eedf 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailListTile extends StatelessWidget { - const AlbumThumbnailListTile({ - super.key, - required this.album, - this.onTap, - }); + const AlbumThumbnailListTile({super.key, required this.album, this.onTap}); final Album album; final void Function()? onTap; @@ -26,15 +22,11 @@ class AlbumThumbnailListTile extends StatelessWidget { buildEmptyThumbnail() { return Container( - decoration: BoxDecoration( - color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200]), child: SizedBox( height: cardSize, width: cardSize, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ), ); } @@ -45,10 +37,7 @@ class AlbumThumbnailListTile extends StatelessWidget { height: cardSize, fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 200), - imageUrl: getAlbumThumbnailUrl( - album, - type: AssetMediaSize.thumbnail, - ), + imageUrl: getAlbumThumbnailUrl(album, type: AssetMediaSize.thumbnail), httpHeaders: ApiService.getRequestHeaders(), cacheKey: getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), @@ -57,7 +46,8 @@ class AlbumThumbnailListTile extends StatelessWidget { return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: onTap ?? + onTap: + onTap ?? () { context.pushRoute(AlbumViewerRoute(albumId: album.id)); }, @@ -79,37 +69,18 @@ class AlbumThumbnailListTile extends StatelessWidget { Text( album.name, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( - 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), - style: const TextStyle( - fontSize: 12, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: const TextStyle(fontSize: 12), ), if (album.shared) ...[ - const Text( - ' • ', - style: TextStyle( - fontSize: 12, - ), - ), - Text( - 'shared'.tr(), - style: const TextStyle( - fontSize: 12, - ), - ), + const Text(' • ', style: TextStyle(fontSize: 12)), + Text('shared'.tr(), style: const TextStyle(fontSize: 12)), ], ], ), diff --git a/mobile/lib/widgets/album/album_title_text_field.dart b/mobile/lib/widgets/album/album_title_text_field.dart index 7807a6e6ae..0a7438b7ae 100644 --- a/mobile/lib/widgets/album/album_title_text_field.dart +++ b/mobile/lib/widgets/album/album_title_text_field.dart @@ -31,11 +31,7 @@ class AlbumTitleTextField extends ConsumerWidget { ref.watch(albumTitleProvider.notifier).setAlbumTitle(v); }, focusNode: albumTitleTextFieldFocusNode, - style: TextStyle( - fontSize: 28, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; @@ -52,24 +48,17 @@ class AlbumTitleTextField extends ConsumerWidget { albumTitleController.clear(); isAlbumTitleEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), hintText: 'add_a_title'.tr(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( diff --git a/mobile/lib/widgets/album/album_viewer_appbar.dart b/mobile/lib/widgets/album/album_viewer_appbar.dart index f13f1c3b21..420218d7e5 100644 --- a/mobile/lib/widgets/album/album_viewer_appbar.dart +++ b/mobile/lib/widgets/album/album_viewer_appbar.dart @@ -82,10 +82,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onPressed: () => context.pop('Cancel'), child: Text( 'cancel', - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( @@ -95,10 +92,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge }, child: Text( 'confirm', - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.colorScheme.error, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.colorScheme.error), ).tr(), ), ], @@ -128,10 +122,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge album.ownerId == userId ? ListTile( leading: const Icon(Icons.delete_forever_rounded), - title: const Text( - 'delete_album', - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text('delete_album', style: TextStyle(fontWeight: FontWeight.w500)).tr(), onTap: onDeleteAlbumPressed, ) : ListTile( @@ -172,18 +163,12 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onAddUsers(); } }, - title: const Text( - "album_viewer_page_share_add_users", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("album_viewer_page_share_add_users", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.swap_vert_rounded), onTap: onSortOrderToggled, - title: const Text( - "change_display_order", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("change_display_order", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.link_rounded), @@ -191,18 +176,12 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge context.pushRoute(SharedLinkEditRoute(albumId: album.remoteId)); context.pop(); }, - title: const Text( - "control_bottom_app_bar_share_link", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("control_bottom_app_bar_share_link", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.settings_rounded), onTap: () => context.navigateTo(const AlbumOptionsRoute()), - title: const Text( - "options", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("options", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; @@ -216,10 +195,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onAddPhotos(); } }, - title: const Text( - "add_photos", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("add_photos", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; showModalBottomSheet( @@ -250,18 +226,13 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.mode_comment_outlined, - ), + const Icon(Icons.mode_comment_outlined), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), ], @@ -285,8 +256,9 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge } titleFocusNode.unfocus(); } else if (newAlbumDescription.isNotEmpty) { - bool isSuccessDescription = - await ref.watch(albumViewerProvider.notifier).changeAlbumDescription(album, newAlbumDescription); + bool isSuccessDescription = await ref + .watch(albumViewerProvider.notifier) + .changeAlbumDescription(album, newAlbumDescription); if (!isSuccessDescription) { ImmichToast.show( context: context, @@ -322,11 +294,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge actions: [ if (album.shared && (album.activityEnabled || comments != 0)) buildActivitiesButton(), if (album.isRemote) ...[ - IconButton( - splashRadius: 25, - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_horiz_rounded), - ), + IconButton(splashRadius: 25, onPressed: buildBottomSheet, icon: const Icon(Icons.more_horiz_rounded)), ], ], ); diff --git a/mobile/lib/widgets/album/album_viewer_editable_description.dart b/mobile/lib/widgets/album/album_viewer_editable_description.dart index 94f41dc7fd..decd268ff3 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_description.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_description.dart @@ -8,11 +8,7 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableDescription extends HookConsumerWidget { final String albumDescription; final FocusNode descriptionFocusNode; - const AlbumViewerEditableDescription({ - super.key, - required this.albumDescription, - required this.descriptionFocusNode, - }); + const AlbumViewerEditableDescription({super.key, required this.albumDescription, required this.descriptionFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,15 +27,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { } } - useEffect( - () { - descriptionFocusNode.addListener(onFocusModeChange); - return () { - descriptionFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + descriptionFocusNode.addListener(onFocusModeChange); + return () { + descriptionFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -72,19 +65,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onPressed: () { descriptionTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: descriptionFocusNode.hasFocus, diff --git a/mobile/lib/widgets/album/album_viewer_editable_title.dart b/mobile/lib/widgets/album/album_viewer_editable_title.dart index b64be09ff9..c84e613017 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_title.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_title.dart @@ -8,11 +8,7 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { final String albumName; final FocusNode titleFocusNode; - const AlbumViewerEditableTitle({ - super.key, - required this.albumName, - required this.titleFocusNode, - }); + const AlbumViewerEditableTitle({super.key, required this.albumName, required this.titleFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,15 +27,12 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } } - useEffect( - () { - titleFocusNode.addListener(onFocusModeChange); - return () { - titleFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + titleFocusNode.addListener(onFocusModeChange); + return () { + titleFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -51,9 +44,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, focusNode: titleFocusNode, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), + style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700), controller: titleTextEditController, onTap: () { context.focusScope.requestFocus(titleFocusNode); @@ -66,35 +57,23 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0), suffixIcon: titleFocusNode.hasFocus ? IconButton( onPressed: () { titleTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: titleFocusNode.hasFocus, hintText: 'add_a_title'.tr(), - hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( - fontSize: 28, - ), + hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(fontSize: 28), ), ), ); diff --git a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart index f7f3f62b32..7be5db1798 100644 --- a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart +++ b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dar import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class RemoteAlbumSharedUserIcons extends ConsumerWidget { - const RemoteAlbumSharedUserIcons({ - super.key, - }); + const RemoteAlbumSharedUserIcons({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,12 +29,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 4.0), - child: UserCircleAvatar( - user: sharedUsers[index], - radius: 18, - size: 36, - hasBorder: true, - ), + child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), ); }), itemCount: sharedUsers.length, diff --git a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart index e485763114..b21e86d145 100644 --- a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart +++ b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart @@ -14,15 +14,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { onTap: () { // debugPrint("View ${asset.id}"); }, - child: Stack( - children: [ - ImmichThumbnail( - asset: asset, - width: 500, - height: 500, - ), - ], - ), + child: Stack(children: [ImmichThumbnail(asset: asset, width: 500, height: 500)]), ); } } diff --git a/mobile/lib/widgets/asset_grid/asset_drag_region.dart b/mobile/lib/widgets/asset_grid/asset_drag_region.dart index f27fae64e8..71e55acbd6 100644 --- a/mobile/lib/widgets/asset_grid/asset_drag_region.dart +++ b/mobile/lib/widgets/asset_grid/asset_drag_region.dart @@ -163,12 +163,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { final int rowIndex; final int sectionIndex; - const AssetIndexWrapper({ - required Widget super.child, - required this.rowIndex, - required this.sectionIndex, - super.key, - }); + const AssetIndexWrapper({required Widget super.child, required this.rowIndex, required this.sectionIndex, super.key}); @override // ignore: library_private_types_in_public_api @@ -191,19 +186,14 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { class _AssetIndexProxy extends RenderProxyBox { AssetIndex index; - _AssetIndexProxy({ - required this.index, - }); + _AssetIndexProxy({required this.index}); } class AssetIndex { final int rowIndex; final int sectionIndex; - const AssetIndex({ - required this.rowIndex, - required this.sectionIndex, - }); + const AssetIndex({required this.rowIndex, required this.sectionIndex}); @override bool operator ==(covariant AssetIndex other) { diff --git a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart index 88b993a026..d95d6efe2e 100644 --- a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart @@ -8,12 +8,7 @@ import 'package:logging/logging.dart'; final log = Logger('AssetGridDataStructure'); -enum RenderAssetGridElementType { - assets, - assetRow, - groupDividerTitle, - monthTitle; -} +enum RenderAssetGridElementType { assets, assetRow, groupDividerTitle, monthTitle } class RenderAssetGridElement { final RenderAssetGridElementType type; @@ -33,13 +28,7 @@ class RenderAssetGridElement { }); } -enum GroupAssetsBy { - day, - month, - auto, - none, - ; -} +enum GroupAssetsBy { day, month, auto, none } class RenderList { final List elements; @@ -87,10 +76,7 @@ class RenderList { // when scrolling backward, end shortly after the requested offset... // ... to guard against the user scrolling in the other direction // a tiny bit resulting in a another required load from the DB - final start = max( - 0, - forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len), - ); + final start = max(0, forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len)); // load the calculated batch (start:start+len) from the DB and put it into the buffer _buf = query!.offset(start).limit(len).findAllSync(); _bufOffset = start; @@ -117,19 +103,14 @@ class RenderList { // request the asset from the database (not changing the buffer!) final asset = query!.offset(index).findFirstSync(); if (asset == null) { - throw Exception( - "Asset at index $index does no longer exist in database", - ); + throw Exception("Asset at index $index does no longer exist in database"); } return asset; } throw Exception("RenderList has neither assets nor query"); } - static Future fromQuery( - QueryBuilder query, - GroupAssetsBy groupBy, - ) => + static Future fromQuery(QueryBuilder query, GroupAssetsBy groupBy) => _buildRenderList(null, query, groupBy); static Future _buildRenderList( @@ -145,12 +126,7 @@ class RenderList { if (groupBy == GroupAssetsBy.none) { final int total = assets?.length ?? query!.countSync(); - final dateLoader = query != null - ? DateBatchLoader( - query: query, - batchSize: 1000 * sectionSize, - ) - : null; + final dateLoader = query != null ? DateBatchLoader(query: query, batchSize: 1000 * sectionSize) : null; for (int i = 0; i < total; i += sectionSize) { final date = assets != null ? assets[i].fileCreatedAt : await dateLoader?.getDate(i); @@ -224,11 +200,11 @@ class RenderList { for (int j = 0; j < count; j += sectionSize) { final type = j == 0 ? (groupBy != GroupAssetsBy.month && newMonth - ? RenderAssetGridElementType.monthTitle - : RenderAssetGridElementType.groupDividerTitle) + ? RenderAssetGridElementType.monthTitle + : RenderAssetGridElementType.groupDividerTitle) : (groupBy == GroupAssetsBy.auto - ? RenderAssetGridElementType.groupDividerTitle - : RenderAssetGridElementType.assets); + ? RenderAssetGridElementType.groupDividerTitle + : RenderAssetGridElementType.assets); final sectionCount = j + sectionSize > count ? count - j : sectionSize; assert(sectionCount > 0 && sectionCount <= sectionSize); elements.add( @@ -257,11 +233,7 @@ class RenderList { : await query!.offset(offset).limit(pageSize).fileCreatedAtProperty().findAll(); int i = 0; for (final date in dates) { - final d = DateTime( - date.year, - date.month, - groupBy == GroupAssetsBy.month ? 1 : date.day, - ); + final d = DateTime(date.year, date.month, groupBy == GroupAssetsBy.month ? 1 : date.day); current ??= d; if (current != d) { addElems(current, prevDate); @@ -288,10 +260,7 @@ class RenderList { static RenderList empty() => RenderList([], null, []); - static Future fromAssets( - List assets, - GroupAssetsBy groupBy, - ) => + static Future fromAssets(List assets, GroupAssetsBy groupBy) => _buildRenderList(assets, null, groupBy); /// Deletes an asset from the render list and clears the buffer @@ -310,10 +279,7 @@ class DateBatchLoader { List _buffer = []; int _bufferStart = 0; - DateBatchLoader({ - required this.query, - required this.batchSize, - }); + DateBatchLoader({required this.query, required this.batchSize}); Future getDate(int index) async { if (!_isIndexInBuffer(index)) { diff --git a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart index e265162850..b1e54af62a 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -81,43 +81,26 @@ class ControlBottomAppBar extends HookConsumerWidget { final isInLockedView = ref.watch(inLockedViewProvider); void minimize() { - scrollController.animateTo( - bottomPadding, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - ); + scrollController.animateTo(bottomPadding, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } - useEffect( - () { - controlBottomAppBarNotifier.addListener(minimize); - return () { - controlBottomAppBarNotifier.removeListener(minimize); - }; - }, - [], - ); + useEffect(() { + controlBottomAppBarNotifier.addListener(minimize); + return () { + controlBottomAppBarNotifier.removeListener(minimize); + }; + }, []); - void showForceDeleteDialog( - Function(bool) deleteCb, { - String? alertMsg, - }) { + void showForceDeleteDialog(Function(bool) deleteCb, {String? alertMsg}) { showDialog( context: context, builder: (BuildContext context) { - return DeleteDialog( - alert: alertMsg, - onDelete: () => deleteCb(true), - ); + return DeleteDialog(alert: alertMsg, onDelete: () => deleteCb(true)); }, ); } - void handleRemoteDelete( - bool force, - Function(bool) deleteCb, { - String? alertMsg, - }) { + void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) { if (!force) { deleteCb(force); return; @@ -153,11 +136,7 @@ class ControlBottomAppBar extends HookConsumerWidget { if (hasRemote && onDownload != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), - child: ControlBoxButton( - iconData: Icons.download, - label: "download".tr(), - onPressed: onDownload, - ), + child: ControlBoxButton(iconData: Icons.download, label: "download".tr(), onPressed: onDownload), ), if (hasLocal && hasRemote && onDelete != null && !isInLockedView) ConstrainedBox( @@ -178,17 +157,10 @@ class ControlBottomAppBar extends HookConsumerWidget { ? "control_bottom_app_bar_trash_from_immich".tr() : "control_bottom_app_bar_delete_from_immich".tr(), onPressed: enabled - ? () => handleRemoteDelete( - !trashEnabled, - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => handleRemoteDelete(!trashEnabled, onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, onLongPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -199,10 +171,7 @@ class ControlBottomAppBar extends HookConsumerWidget { iconData: Icons.delete_forever, label: "delete_dialog_title".tr(), onPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -221,9 +190,7 @@ class ControlBottomAppBar extends HookConsumerWidget { showDialog( context: context, builder: (BuildContext context) { - return DeleteLocalOnlyDialog( - onDeleteLocal: onDeleteLocal!, - ); + return DeleteLocalOnlyDialog(onDeleteLocal: onDeleteLocal!); }, ); } @@ -281,13 +248,11 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "upload".tr(), onPressed: enabled ? () => showDialog( - context: context, - builder: (BuildContext context) { - return UploadDialog( - onUpload: onUpload, - ); - }, - ) + context: context, + builder: (BuildContext context) { + return UploadDialog(onUpload: onUpload); + }, + ) : null, ), ]; @@ -325,10 +290,7 @@ class ControlBottomAppBar extends HookConsumerWidget { surfaceTintColor: context.colorScheme.surfaceContainerHigh, elevation: 6.0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), ), margin: const EdgeInsets.all(0), child: CustomScrollView( @@ -349,14 +311,8 @@ class ControlBottomAppBar extends HookConsumerWidget { ), ), if (hasRemote && !isInLockedView) ...[ - const Divider( - indent: 16, - endIndent: 16, - thickness: 1, - ), - _AddToAlbumTitleRow( - onCreateNewAlbum: enabled ? onCreateNewAlbum : null, - ), + const Divider(indent: 16, endIndent: 16, thickness: 1), + _AddToAlbumTitleRow(onCreateNewAlbum: enabled ? onCreateNewAlbum : null), ], ], ), @@ -380,9 +336,7 @@ class ControlBottomAppBar extends HookConsumerWidget { } class _AddToAlbumTitleRow extends StatelessWidget { - const _AddToAlbumTitleRow({ - required this.onCreateNewAlbum, - }); + const _AddToAlbumTitleRow({required this.onCreateNewAlbum}); final VoidCallback? onCreateNewAlbum; @@ -393,23 +347,13 @@ class _AddToAlbumTitleRow extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( onPressed: onCreateNewAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart index ecfb4130dc..e7c7775e54 100644 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/delete_dialog.dart @@ -5,22 +5,19 @@ import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; class DeleteDialog extends ConfirmDialog { const DeleteDialog({super.key, String? alert, required Function onDelete}) - : super( - title: "delete_dialog_title", - content: alert ?? "delete_dialog_alert", - cancel: "cancel", - ok: "delete", - onOk: onDelete, - ); + : super( + title: "delete_dialog_title", + content: alert ?? "delete_dialog_alert", + cancel: "cancel", + ok: "delete", + onOk: onDelete, + ); } class DeleteLocalOnlyDialog extends StatelessWidget { final void Function(bool onlyMerged) onDeleteLocal; - const DeleteLocalOnlyDialog({ - super.key, - required this.onDeleteLocal, - }); + const DeleteLocalOnlyDialog({super.key, required this.onDeleteLocal}); @override Widget build(BuildContext context) { @@ -35,9 +32,7 @@ class DeleteLocalOnlyDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: const Text("delete_dialog_title").tr(), content: const Text("delete_dialog_alert_local_non_backed_up").tr(), actions: [ @@ -45,30 +40,21 @@ class DeleteLocalOnlyDialog extends StatelessWidget { onPressed: () => context.pop(), child: Text( "cancel", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onDeleteBackedUpOnly, child: Text( "delete_local_dialog_ok_backed_up_only", - style: TextStyle( - color: context.colorScheme.tertiary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onForceDelete, child: Text( "delete_local_dialog_ok_force", - style: TextStyle( - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart index 50b38c2a4a..93a1d53f4e 100644 --- a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart +++ b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart @@ -3,11 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class DisableMultiSelectButton extends ConsumerWidget { - const DisableMultiSelectButton({ - super.key, - required this.onPressed, - required this.selectedItemCount, - }); + const DisableMultiSelectButton({super.key, required this.onPressed, required this.selectedItemCount}); final Function onPressed; final int selectedItemCount; @@ -22,16 +18,10 @@ class DisableMultiSelectButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 4.0), child: ElevatedButton.icon( onPressed: () => onPressed(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( '$selectedItemCount', - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ), ), diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart index ffe8c54320..3de52c2816 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart @@ -3,14 +3,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(double offsetY); @@ -79,8 +80,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.arrows({ super.key, @@ -95,8 +96,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.semicircle({ super.key, @@ -111,12 +112,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -149,17 +146,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -180,9 +170,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -198,9 +186,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbArrowBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbArrowBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -216,9 +202,7 @@ class DraggableScrollbar extends StatefulWidget { width: 20.0, decoration: BoxDecoration( color: backgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(12.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), ), ); @@ -235,9 +219,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbRRectBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbRRectBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -250,11 +232,7 @@ class DraggableScrollbar extends StatefulWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(7.0)), - child: Container( - constraints: BoxConstraints.tight( - Size(16.0, height), - ), - ), + child: Container(constraints: BoxConstraints.tight(Size(16.0, height))), ); return buildScrollThumbAndLabel( @@ -296,11 +274,7 @@ class ScrollLabel extends StatelessWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Container( - constraints: constraints ?? _defaultConstraints, - alignment: Alignment.center, - child: child, - ), + child: Container(constraints: constraints ?? _defaultConstraints, alignment: Alignment.center, child: child), ), ), ); @@ -325,25 +299,13 @@ class DraggableScrollbarState extends State with TickerProvi _viewOffset = 0.0; _isDragInProcess = false; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -366,9 +328,7 @@ class DraggableScrollbarState extends State with TickerProvi Widget build(BuildContext context) { Text? labelText; if (widget.labelTextBuilder != null && _isDragInProcess) { - labelText = widget.labelTextBuilder!( - _viewOffset + _barOffset + widget.heightScrollThumb / 2, - ); + labelText = widget.labelTextBuilder!(_viewOffset + _barOffset + widget.heightScrollThumb / 2); } return LayoutBuilder( @@ -382,9 +342,7 @@ class DraggableScrollbarState extends State with TickerProvi }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -422,11 +380,7 @@ class DraggableScrollbarState extends State with TickerProvi setState(() { if (notification is ScrollUpdateNotification) { - _barOffset += getBarDelta( - notification.scrollDelta!, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + _barOffset += getBarDelta(notification.scrollDelta!, barMaxScrollExtent, viewMaxScrollExtent); if (_barOffset < barMinScrollExtent) { _barOffset = barMinScrollExtent; @@ -459,19 +413,11 @@ class DraggableScrollbarState extends State with TickerProvi }); } - double getBarDelta( - double scrollViewDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getBarDelta(double scrollViewDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return scrollViewDelta * barMaxScrollExtent / viewMaxScrollExtent; } - double getScrollViewDelta( - double barDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getScrollViewDelta(double barDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return barDelta * viewMaxScrollExtent / barMaxScrollExtent; } @@ -498,11 +444,7 @@ class DraggableScrollbarState extends State with TickerProvi _barOffset = barMaxScrollExtent; } - double viewDelta = getScrollViewDelta( - details.delta.dy, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -545,14 +487,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -583,10 +519,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -595,10 +528,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -613,11 +543,7 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { @@ -625,14 +551,8 @@ class SlideFadeTransition extends StatelessWidget { animation: animation, builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart index 63fa3be763..17f35311f0 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(int item); @@ -75,12 +76,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -113,17 +110,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -144,9 +134,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -219,25 +207,13 @@ class DraggableScrollbarState extends State with TickerProvi _isDragInProcess = false; _currentItem = 0; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -272,9 +248,7 @@ class DraggableScrollbarState extends State with TickerProvi }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -370,16 +344,12 @@ class DraggableScrollbarState extends State with TickerProvi /// If the bar is at the bottom but the item position is still smaller than the max item count (due to rounding error) /// jump to the end of the list if (barMaxScrollExtent - _barOffset < 10 && itemPosition < maxItemCount) { - widget.controller.jumpTo( - index: maxItemCount, - ); + widget.controller.jumpTo(index: maxItemCount); return; } - widget.controller.jumpTo( - index: itemPosition, - ); + widget.controller.jumpTo(index: itemPosition); } Timer? dragHaltTimer; @@ -405,12 +375,9 @@ class DraggableScrollbarState extends State with TickerProvi dragHaltTimer?.cancel(); widget.scrollStateListener(true); - dragHaltTimer = Timer( - const Duration(milliseconds: 500), - () { - widget.scrollStateListener(false); - }, - ); + dragHaltTimer = Timer(const Duration(milliseconds: 500), () { + widget.scrollStateListener(false); + }); } _jumpToBarPosition(); @@ -451,14 +418,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -489,10 +450,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -501,10 +459,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -519,11 +474,7 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { @@ -531,14 +482,8 @@ class SlideFadeTransition extends StatelessWidget { animation: animation, builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/group_divider_title.dart b/mobile/lib/widgets/asset_grid/group_divider_title.dart index 81f9392d37..1464c941f0 100644 --- a/mobile/lib/widgets/asset_grid/group_divider_title.dart +++ b/mobile/lib/widgets/asset_grid/group_divider_title.dart @@ -30,13 +30,10 @@ class GroupDividerTitle extends HookConsumerWidget { final appSettingService = ref.watch(appSettingsServiceProvider); final groupBy = useState(GroupAssetsBy.day); - useEffect( - () { - groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; - return null; - }, - [], - ); + useEffect(() { + groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return null; + }, []); void handleTitleIconClick() { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -59,9 +56,7 @@ class GroupDividerTitle extends HookConsumerWidget { Text( text, style: groupBy.value == GroupAssetsBy.month - ? context.textTheme.bodyLarge?.copyWith( - fontSize: 24.0, - ) + ? context.textTheme.bodyLarge?.copyWith(fontSize: 24.0) : context.textTheme.labelLarge?.copyWith( color: context.textTheme.labelLarge?.color?.withAlpha(250), fontWeight: FontWeight.w500, diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index 112d8074ad..ab6b350a7b 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -60,9 +60,7 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var settings = ref.watch(appSettingsServiceProvider); - final perRow = useState( - assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!, - ); + final perRow = useState(assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!); final scaleFactor = useState(7.0 - perRow.value); final baseScaleFactor = useState(7.0 - perRow.value); @@ -82,22 +80,21 @@ class ImmichAssetGrid extends HookConsumerWidget { return RawGestureDetector( gestures: { CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( - () => CustomScaleGestureRecognizer(), (CustomScaleGestureRecognizer scale) { - scale.onStart = (details) { - baseScaleFactor.value = scaleFactor.value; - }; + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + baseScaleFactor.value = scaleFactor.value; + }; - scale.onUpdate = (details) { - scaleFactor.value = max( - min(5.0, baseScaleFactor.value * details.scale), - 1.0, - ); - if (7 - scaleFactor.value.toInt() != perRow.value) { - perRow.value = 7 - scaleFactor.value.toInt(); - settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); - } - }; - }), + scale.onUpdate = (details) { + scaleFactor.value = max(min(5.0, baseScaleFactor.value * details.scale), 1.0); + if (7 - scaleFactor.value.toInt() != perRow.value) { + perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); + } + }; + }, + ), }, child: ImmichAssetGridView( onRefresh: onRefresh, @@ -125,9 +122,7 @@ class ImmichAssetGrid extends HookConsumerWidget { if (renderList != null) return buildAssetGridView(renderList!); final renderListFuture = ref.watch(assetsTimelineProvider(assets!)); - return renderListFuture.widgetWhen( - onData: (renderList) => buildAssetGridView(renderList), - ); + return renderListFuture.widgetWhen(onData: (renderList) => buildAssetGridView(renderList)); } } diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart index ccea8307ae..7db03a33aa 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -34,10 +34,7 @@ import 'disable_multi_select_button.dart'; import 'draggable_scrollbar_custom.dart'; import 'group_divider_title.dart'; -typedef ImmichAssetGridSelectionListener = void Function( - bool, - Set, -); +typedef ImmichAssetGridSelectionListener = void Function(bool, Set); class ImmichAssetGridView extends ConsumerStatefulWidget { final RenderList renderList; @@ -161,16 +158,10 @@ class ImmichAssetGridViewState extends ConsumerState { // the scroll_position widget crashes. This is a workaround to prevent this. // If the index is within the last 10 elements, we jump instead of scrolling. if (widget.renderList.elements.length <= index + 10) { - _itemScrollController.jumpTo( - index: index, - ); + _itemScrollController.jumpTo(index: index); return; } - await _itemScrollController.scrollTo( - index: index, - alignment: 0, - duration: const Duration(milliseconds: 500), - ); + await _itemScrollController.scrollTo(index: index, alignment: 0, duration: const Duration(milliseconds: 500)); } Widget _itemBuilder(BuildContext c, int position) { @@ -219,18 +210,12 @@ class ImmichAssetGridViewState extends ConsumerState { return Text( DateFormat.yMMMM().format(date), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ); } Widget _buildMultiSelectIndicator() { - return DisableMultiSelectButton( - onPressed: () => _deselectAll(), - selectedItemCount: _selectedAssets.length, - ); + return DisableMultiSelectButton(onPressed: () => _deselectAll(), selectedItemCount: _selectedAssets.length); } Widget _buildAssetGrid() { @@ -250,10 +235,7 @@ class ImmichAssetGridViewState extends ConsumerState { } final listWidget = ScrollablePositionedList.builder( - padding: EdgeInsets.only( - top: appBarOffset() ? 60 : 0, - bottom: 220, - ), + padding: EdgeInsets.only(top: appBarOffset() ? 60 : 0, bottom: 220), itemBuilder: _itemBuilder, itemPositionsListener: _itemPositionsListener, physics: _scrollPhysics, @@ -269,8 +251,9 @@ class ImmichAssetGridViewState extends ConsumerState { scrollStateListener: dragScrolling, itemPositionsListener: _itemPositionsListener, controller: _itemScrollController, - backgroundColor: - context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary, + backgroundColor: context.isDarkTheme + ? context.colorScheme.primary.darken(amount: .5) + : context.colorScheme.primary, labelTextBuilder: widget.showLabel ? _labelBuilder : null, padding: appBarOffset() ? const EdgeInsets.only(top: 60) : const EdgeInsets.only(), heightOffset: appBarOffset() ? 60 : 0, @@ -284,12 +267,8 @@ class ImmichAssetGridViewState extends ConsumerState { return widget.onRefresh == null ? child : appBarOffset() - ? RefreshIndicator( - onRefresh: widget.onRefresh!, - edgeOffset: 30, - child: child, - ) - : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); + ? RefreshIndicator(onRefresh: widget.onRefresh!, edgeOffset: 30, child: child) + : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); } void _scrollToDate() { @@ -312,9 +291,7 @@ class ImmichAssetGridViewState extends ConsumerState { // If the exact date is not found, the timeline is grouped by month, // thus we search for the month if (index == -1) { - index = widget.renderList.elements.indexWhere( - (e) => e.date.year == date.year && e.date.month == date.month, - ); + index = widget.renderList.elements.indexWhere((e) => e.date.year == date.year && e.date.month == date.month); } if (index < widget.renderList.elements.length) { @@ -411,13 +388,8 @@ class ImmichAssetGridViewState extends ConsumerState { void _scrollToTop() { // for some reason, this is necessary as well in order // to correctly reposition the drag thumb scroll bar - _itemScrollController.jumpTo( - index: 0, - ); - _itemScrollController.scrollTo( - index: 0, - duration: const Duration(milliseconds: 200), - ); + _itemScrollController.jumpTo(index: 0); + _itemScrollController.scrollTo(index: 0, duration: const Duration(milliseconds: 200)); } void _setDragStartIndex(AssetIndex index) { @@ -495,9 +467,7 @@ class ImmichAssetGridViewState extends ConsumerState { final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (currentSectionIndex == startSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, sectionAssets.length), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, sectionAssets.length)); } else { selectedAssets.addAll(sectionAssets); } @@ -509,13 +479,9 @@ class ImmichAssetGridViewState extends ConsumerState { if (section != null) { final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (startSectionIndex == endSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1)); } else { - selectedAssets.addAll( - sectionAssets.slice(0, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(0, endSectionAssetIndex + 1)); } } @@ -554,9 +520,8 @@ class ImmichAssetGridViewState extends ConsumerState { onAssetEnter: _handleDragAssetEnter, onEnd: _stopDrag, onScroll: _dragDragScroll, - onScrollStart: () => WidgetsBinding.instance.addPostFrameCallback( - (_) => controlBottomAppBarNotifier.minimize(), - ), + onScrollStart: () => + WidgetsBinding.instance.addPostFrameCallback((_) => controlBottomAppBarNotifier.minimize()), child: _buildAssetGrid(), ), if (widget.showMultiSelectIndicator && widget.selectionActive) _buildMultiSelectIndicator(), @@ -590,10 +555,7 @@ class _PlaceholderRow extends StatelessWidget { key: ValueKey(i), width: width, height: height, - margin: EdgeInsets.only( - bottom: margin, - right: i + 1 == number ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: i + 1 == number ? 0.0 : margin), ), ], ); @@ -639,9 +601,7 @@ class _Section extends StatelessWidget { }); @override - Widget build( - BuildContext context, - ) { + Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth / assetsPerRow - margin * (assetsPerRow - 1) / assetsPerRow; @@ -675,10 +635,7 @@ class _Section extends StatelessWidget { key: ValueKey(i), rowStartIndex: i * assetsPerRow, sectionIndex: sectionIndex, - assets: assetsToRender.nestedSlice( - i * assetsPerRow, - min((i + 1) * assetsPerRow, section.count), - ), + assets: assetsToRender.nestedSlice(i * assetsPerRow, min((i + 1) * assetsPerRow, section.count)), absoluteOffset: section.offset + i * assetsPerRow, width: width, assetsPerRow: assetsPerRow, @@ -706,9 +663,7 @@ class _Section extends StatelessWidget { class _MonthTitle extends StatelessWidget { final DateTime date; - const _MonthTitle({ - required this.date, - }); + const _MonthTitle({required this.date}); @override Widget build(BuildContext context) { @@ -719,10 +674,7 @@ class _MonthTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( toBeginningOfSentenceCase(title, context.locale.languageCode), - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w500), ), ); } @@ -821,11 +773,7 @@ class _AssetRow extends StatelessWidget { // Normalize: final sum = arConfiguration.sum; - widthDistribution.setRange( - 0, - widthDistribution.length, - arConfiguration.map((e) => (e * assets.length) / sum), - ); + widthDistribution.setRange(0, widthDistribution.length, arConfiguration.map((e) => (e * assets.length) / sum)); } return Row( key: key, @@ -835,10 +783,7 @@ class _AssetRow extends StatelessWidget { return Container( width: width * widthDistribution[index], height: width, - margin: EdgeInsets.only( - bottom: margin, - right: last ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: last ? 0.0 : margin), child: GestureDetector( onTap: () { if (selectionActive) { diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index a7c1290b30..c28f407e1e 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -79,53 +79,38 @@ class MultiselectGrid extends HookConsumerWidget { final currentUser = ref.watch(currentUserProvider); final processing = useProcessingOverlay(); - useEffect( - () { - selectionEnabledHook.addListener(() { - multiselectEnabled.state = selectionEnabledHook.value; - }); + useEffect(() { + selectionEnabledHook.addListener(() { + multiselectEnabled.state = selectionEnabledHook.value; + }); - return () { - // This does not work in tests - if (kReleaseMode) { - selectionEnabledHook.dispose(); - } - }; - }, - [], - ); + return () { + // This does not work in tests + if (kReleaseMode) { + selectionEnabledHook.dispose(); + } + }; + }, []); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; selectionAssetState.value = AssetSelectionState.fromSelection(selectedAssets); } errorBuilder(String? msg) => msg != null && msg.isNotEmpty - ? () => ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ) + ? () => ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM) : null; - Iterable ownedRemoteSelection({ - String? localErrorMessage, - String? ownerErrorMessage, - }) { + Iterable ownedRemoteSelection({String? localErrorMessage, String? ownerErrorMessage}) { final assets = selection.value; - return assets.remoteOnly(errorCallback: errorBuilder(localErrorMessage)).ownedOnly( - currentUser, - errorCallback: errorBuilder(ownerErrorMessage), - ); + return assets + .remoteOnly(errorCallback: errorBuilder(localErrorMessage)) + .ownedOnly(currentUser, errorCallback: errorBuilder(ownerErrorMessage)); } - Iterable remoteSelection({String? errorMessage}) => selection.value.remoteOnly( - errorCallback: errorBuilder(errorMessage), - ); + Iterable remoteSelection({String? errorMessage}) => + selection.value.remoteOnly(errorCallback: errorBuilder(errorMessage)); void onShareAssets(bool shareLocal) { processing.value = true; @@ -174,10 +159,7 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; try { final toDelete = selection.value - .ownedOnly( - currentUser, - errorCallback: errorBuilder('home_page_delete_err_partner'.tr()), - ) + .ownedOnly(currentUser, errorCallback: errorBuilder('home_page_delete_err_partner'.tr())) .toList(); final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(toDelete, force: force); @@ -237,25 +219,10 @@ class MultiselectGrid extends HookConsumerWidget { final failedCount = totalCount - successCount; final msg = failedCount > 0 - ? 'assets_downloaded_failed'.t( - context: context, - args: { - 'count': successCount, - 'error': failedCount, - }, - ) - : 'assets_downloaded_successfully'.t( - context: context, - args: { - 'count': successCount, - }, - ); + ? 'assets_downloaded_failed'.t(context: context, args: {'count': successCount, 'error': failedCount}) + : 'assets_downloaded_successfully'.t(context: context, args: {'count': successCount}); - ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -270,10 +237,9 @@ class MultiselectGrid extends HookConsumerWidget { ownerErrorMessage: 'home_page_delete_err_partner'.tr(), ).toList(); - final isDeleted = await ref.read(assetProvider.notifier).deleteRemoteAssets( - toDelete, - shouldDeletePermanently: shouldDeletePermanently, - ); + final isDeleted = await ref + .read(assetProvider.notifier) + .deleteRemoteAssets(toDelete, shouldDeletePermanently: shouldDeletePermanently); if (isDeleted) { ImmichToast.show( context: context, @@ -293,10 +259,9 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; selectionEnabledHook.value = false; try { - ref.read(manualUploadProvider.notifier).uploadAssets( - context, - selection.value.where((a) => a.storage == AssetState.local), - ); + ref + .read(manualUploadProvider.notifier) + .uploadAssets(context, selection.value.where((a) => a.storage == AssetState.local)); } finally { processing.value = false; } @@ -305,16 +270,11 @@ class MultiselectGrid extends HookConsumerWidget { void onAddToAlbum(Album album) async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref.read(albumServiceProvider).addAssets( - album, - assets, - ); + final result = await ref.read(albumServiceProvider).addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { @@ -332,10 +292,7 @@ class MultiselectGrid extends HookConsumerWidget { ImmichToast.show( context: context, msg: "home_page_add_to_album_success".tr( - namedArgs: { - "album": album.name, - "added": result.successfullyAdded.toString(), - }, + namedArgs: {"album": album.name, "added": result.successfullyAdded.toString()}, ), toastType: ToastType.success, ); @@ -350,9 +307,7 @@ class MultiselectGrid extends HookConsumerWidget { void onCreateNewAlbum() async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } @@ -376,9 +331,7 @@ class MultiselectGrid extends HookConsumerWidget { return; } - await ref.read(stackServiceProvider).createStack( - selection.value.map((e) => e.remoteId!).toList(), - ); + await ref.read(stackServiceProvider).createStack(selection.value.map((e) => e.remoteId!).toList()); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -426,12 +379,7 @@ class MultiselectGrid extends HookConsumerWidget { final isInLockedView = ref.read(inLockedViewProvider); final visibility = isInLockedView ? AssetVisibilityEnum.timeline : AssetVisibilityEnum.locked; - await handleSetAssetsVisibility( - ref, - context, - visibility, - remoteAssets.toList(), - ); + await handleSetAssetsVisibility(ref, context, visibility, remoteAssets.toList()); } } finally { processing.value = false; @@ -439,41 +387,34 @@ class MultiselectGrid extends HookConsumerWidget { } } - Future Function() wrapLongRunningFun( - Future Function() fun, { - bool showOverlay = true, - }) => - () async { - if (showOverlay) processing.value = true; - try { - final result = await fun(); - if (result.runtimeType != bool || result == true) { - selectionEnabledHook.value = false; - } - return result; - } finally { - if (showOverlay) processing.value = false; - } - }; + Future Function() wrapLongRunningFun(Future Function() fun, {bool showOverlay = true}) => () async { + if (showOverlay) processing.value = true; + try { + final result = await fun(); + if (result.runtimeType != bool || result == true) { + selectionEnabledHook.value = false; + } + return result; + } finally { + if (showOverlay) processing.value = false; + } + }; return SafeArea( top: true, bottom: false, child: Stack( children: [ - ref.watch(renderListProvider).when( + ref + .watch(renderListProvider) + .when( data: (data) => data.isEmpty && (buildLoadingIndicator != null || topWidget == null) ? (buildLoadingIndicator ?? buildEmptyIndicator)() : ImmichAssetGrid( renderList: data, listener: selectionListener, selectionActive: selectionEnabledHook.value, - onRefresh: onRefresh == null - ? null - : wrapLongRunningFun( - onRefresh!, - showOverlay: false, - ), + onRefresh: onRefresh == null ? null : wrapLongRunningFun(onRefresh!, showOverlay: false), topWidget: topWidget, showStack: stackEnabled, showDragScrollLabel: dragScrollLabelEnabled, @@ -506,9 +447,7 @@ class MultiselectGrid extends HookConsumerWidget { unarchive: unarchive, onToggleLocked: onToggleLockedVisibility, onRemoveFromAlbum: onRemoveFromAlbum != null - ? wrapLongRunningFun( - () => onRemoveFromAlbum!(selection.value), - ) + ? wrapLongRunningFun(() => onRemoveFromAlbum!(selection.value)) : null, ), ], diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart index 10a541cec3..3a1fa82a28 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/providers/asset_viewer/render_list_status_provider import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class MultiselectGridStatusIndicator extends HookConsumerWidget { - const MultiselectGridStatusIndicator({ - super.key, - this.buildLoadingIndicator, - this.emptyIndicator, - }); + const MultiselectGridStatusIndicator({super.key, this.buildLoadingIndicator, this.emptyIndicator}); final Widget Function()? buildLoadingIndicator; final Widget? emptyIndicator; @@ -18,16 +14,13 @@ class MultiselectGridStatusIndicator extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final renderListStatus = ref.watch(renderListStatusProvider); return switch (renderListStatus) { - RenderListStatusEnum.loading => buildLoadingIndicator == null - ? const Center( - child: DelayedLoadingIndicator( - delay: Duration(milliseconds: 500), - ), - ) - : buildLoadingIndicator!(), + RenderListStatusEnum.loading => + buildLoadingIndicator == null + ? const Center(child: DelayedLoadingIndicator(delay: Duration(milliseconds: 500))) + : buildLoadingIndicator!(), RenderListStatusEnum.empty => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), RenderListStatusEnum.error => Center(child: const Text("error_loading_assets").tr()), - RenderListStatusEnum.complete => const SizedBox() + RenderListStatusEnum.complete => const SizedBox(), }; } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 10815d00bb..93385b88b3 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -41,8 +41,9 @@ class ThumbnailImage extends StatelessWidget { @override Widget build(BuildContext context) { - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.6) : context.primaryColor.lighten(amount: 0.8); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.6) + : context.primaryColor.lighten(amount: 0.8); return Stack( children: [ @@ -52,16 +53,13 @@ class ThumbnailImage extends StatelessWidget { decoration: BoxDecoration( border: multiselectEnabled && isSelected ? canDeselect - ? Border.all( - color: assetContainerColor, - width: 8, - ) - : const Border( - top: BorderSide(color: Colors.grey, width: 8), - right: BorderSide(color: Colors.grey, width: 8), - bottom: BorderSide(color: Colors.grey, width: 8), - left: BorderSide(color: Colors.grey, width: 8), - ) + ? Border.all(color: assetContainerColor, width: 8) + : const Border( + top: BorderSide(color: Colors.grey, width: 8), + right: BorderSide(color: Colors.grey, width: 8), + bottom: BorderSide(color: Colors.grey, width: 8), + left: BorderSide(color: Colors.grey, width: 8), + ) : const Border(), ), child: Stack( @@ -76,21 +74,9 @@ class ThumbnailImage extends StatelessWidget { ), if (showStorageIndicator) _StorageIcon(storage: asset.storage), if (asset.isFavorite) - const Positioned( - left: 8, - bottom: 5, - child: Icon( - Icons.favorite, - color: Colors.white, - size: 16, - ), - ), + const Positioned(left: 8, bottom: 5, child: Icon(Icons.favorite, color: Colors.white, size: 16)), if (asset.isVideo) _VideoIcon(duration: asset.duration), - if (asset.stackCount > 0) - _StackIcon( - isVideo: asset.isVideo, - stackCount: asset.stackCount, - ), + if (asset.stackCount > 0) _StackIcon(isVideo: asset.isVideo, stackCount: asset.stackCount), ], ), ), @@ -98,15 +84,9 @@ class ThumbnailImage extends StatelessWidget { isSelected ? const Padding( padding: EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: _SelectedIcon(), - ), + child: Align(alignment: Alignment.topLeft, child: _SelectedIcon()), ) - : const Icon( - Icons.circle_outlined, - color: Colors.white, - ), + : const Icon(Icons.circle_outlined, color: Colors.white), ], ); } @@ -117,18 +97,13 @@ class _SelectedIcon extends StatelessWidget { @override Widget build(BuildContext context) { - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.6) : context.primaryColor.lighten(amount: 0.8); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.6) + : context.primaryColor.lighten(amount: 0.8); return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assetContainerColor, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: assetContainerColor), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } } @@ -147,18 +122,10 @@ class _VideoIcon extends StatelessWidget { children: [ Text( duration.format(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), const SizedBox(width: 3), - const Icon( - Icons.play_circle_fill_rounded, - color: Colors.white, - size: 18, - ), + const Icon(Icons.play_circle_fill_rounded, color: Colors.white, size: 18), ], ), ); @@ -181,21 +148,10 @@ class _StackIcon extends StatelessWidget { if (stackCount > 1) Text( "$stackCount", - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), - if (stackCount > 1) - const SizedBox( - width: 3, - ), - const Icon( - Icons.burst_mode_rounded, - color: Colors.white, - size: 18, - ), + if (stackCount > 1) const SizedBox(width: 3), + const Icon(Icons.burst_mode_rounded, color: Colors.white, size: 18), ], ), ); @@ -211,53 +167,35 @@ class _StorageIcon extends StatelessWidget { Widget build(BuildContext context) { return switch (storage) { AssetState.local => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_off_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_off_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.remote => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.merged => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_done_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_done_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), }; } } @@ -288,13 +226,7 @@ class _ImageIcon extends StatelessWidget { tag: isDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, child: Stack( children: [ - SizedBox.expand( - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, - ), - ), + SizedBox.expand(child: ImmichThumbnail(asset: asset, height: 250, width: 250)), const DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( @@ -321,10 +253,7 @@ class _ImageIcon extends StatelessWidget { return DecoratedBox( decoration: canDeselect ? BoxDecoration(color: assetContainerColor) : const BoxDecoration(color: Colors.grey), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(15.0)), - child: image, - ), + child: ClipRRect(borderRadius: const BorderRadius.all(Radius.circular(15.0)), child: image), ); } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart index 5b12426a50..a84dfbae37 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart @@ -7,12 +7,7 @@ class ThumbnailPlaceholder extends StatelessWidget { final double width; final double height; - const ThumbnailPlaceholder({ - super.key, - this.margin = EdgeInsets.zero, - this.width = 250, - this.height = 250, - }); + const ThumbnailPlaceholder({super.key, this.margin = EdgeInsets.zero, this.width = 250, this.height = 250}); @override Widget build(BuildContext context) { @@ -26,11 +21,7 @@ class ThumbnailPlaceholder extends StatelessWidget { height: height, margin: margin, decoration: BoxDecoration( - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), + gradient: LinearGradient(colors: gradientColors, begin: Alignment.topCenter, end: Alignment.bottomCenter), ), ); } diff --git a/mobile/lib/widgets/asset_grid/upload_dialog.dart b/mobile/lib/widgets/asset_grid/upload_dialog.dart index c2a38fab8c..86e2759566 100644 --- a/mobile/lib/widgets/asset_grid/upload_dialog.dart +++ b/mobile/lib/widgets/asset_grid/upload_dialog.dart @@ -4,11 +4,11 @@ class UploadDialog extends ConfirmDialog { final Function onUpload; const UploadDialog({super.key, required this.onUpload}) - : super( - title: 'upload_dialog_title', - content: 'upload_dialog_info', - cancel: 'cancel', - ok: 'upload', - onOk: onUpload, - ); + : super( + title: 'upload_dialog_title', + content: 'upload_dialog_info', + cancel: 'cancel', + ok: 'upload', + onOk: onUpload, + ); } diff --git a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart index 6fda361632..faa058ced4 100644 --- a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart +++ b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart @@ -8,11 +8,7 @@ class AdvancedBottomSheet extends HookConsumerWidget { final Asset assetDetail; final ScrollController? scrollController; - const AdvancedBottomSheet({ - super.key, - required this.assetDetail, - this.scrollController, - }); + const AdvancedBottomSheet({super.key, required this.assetDetail, this.scrollController}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,27 +22,15 @@ class AdvancedBottomSheet extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Align( - child: Text( - "ADVANCED INFO", - style: TextStyle(fontSize: 12.0), - ), - ), + const Align(child: Text("ADVANCED INFO", style: TextStyle(fontSize: 12.0))), const SizedBox(height: 32.0), Container( decoration: BoxDecoration( color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[200], - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( - padding: const EdgeInsets.only( - right: 16.0, - left: 16, - top: 8, - bottom: 16, - ), + padding: const EdgeInsets.only(right: 16.0, left: 16, top: 8, bottom: 16), child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -55,28 +39,18 @@ class AdvancedBottomSheet extends HookConsumerWidget { alignment: Alignment.centerRight, child: IconButton( onPressed: () { - Clipboard.setData( - ClipboardData( - text: assetDetail.toString(), - ), - ).then((_) { + Clipboard.setData(ClipboardData(text: assetDetail.toString())).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ), SelectableText( diff --git a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart index dd0d95b93b..e7ceac6105 100644 --- a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart +++ b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; /// A widget that animates implicitly between a play and a pause icon. class AnimatedPlayPause extends StatefulWidget { - const AnimatedPlayPause({ - super.key, - required this.playing, - this.size, - this.color, - }); + const AnimatedPlayPause({super.key, required this.playing, this.size, this.color}); final double? size; final bool playing; diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 66392b4bb4..c7125e2200 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -72,10 +72,7 @@ class BottomGalleryBar extends ConsumerWidget { void handleDelete() async { Future onDelete(bool force) async { - final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( - {asset}, - force: force, - ); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets({asset}, force: force); if (isDeleted && isStackPrimaryAsset) { // Workaround for asset remaining in the gallery renderList.deleteAsset(asset); @@ -101,12 +98,7 @@ class BottomGalleryBar extends ConsumerWidget { if (isDeleted) { // Can only trash assets stored in server. Local assets are always permanently removed for now if (context.mounted && asset.isRemote && isStackPrimaryAsset) { - ImmichToast.show( - durationInSecond: 1, - context: context, - msg: 'Asset trashed', - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(durationInSecond: 1, context: context, msg: 'Asset trashed', gravity: ToastGravity.BOTTOM); } removeAssetFromStack(); } @@ -149,19 +141,13 @@ class BottomGalleryBar extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - leading: const Icon( - Icons.filter_none_outlined, - size: 18, - ), + leading: const Icon(Icons.filter_none_outlined, size: 18), onTap: () async { await unStack(); ctx.pop(); context.maybePop(); }, - title: const Text( - "viewer_unstack", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ], ), @@ -189,11 +175,7 @@ class BottomGalleryBar extends ConsumerWidget { context.navigator.push( MaterialPageRoute( - builder: (context) => EditImagePage( - asset: asset, - image: image, - isEdited: false, - ), + builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), ), ); } @@ -221,9 +203,7 @@ class BottomGalleryBar extends ConsumerWidget { return; } - ref.read(downloadStateProvider.notifier).downloadAsset( - asset, - ); + ref.read(downloadStateProvider.notifier).downloadAsset(asset); } handleRemoveFromAlbum() async { @@ -258,12 +238,11 @@ class BottomGalleryBar extends ConsumerWidget { final List> albumActions = [ { BottomNavigationBarItem( - icon: Icon( - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, - ), + icon: Icon(Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded), label: 'share'.tr(), tooltip: 'share'.tr(), - ): (_) => shareAsset(), + ): (_) => + shareAsset(), }, if (asset.isImage && !isInLockedView) { @@ -271,7 +250,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.tune_outlined), label: 'edit'.tr(), tooltip: 'edit'.tr(), - ): (_) => handleEdit(), + ): (_) => + handleEdit(), }, if (isOwner && !isInLockedView) { @@ -285,7 +265,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.archive_outlined), label: 'archive'.tr(), tooltip: 'archive'.tr(), - ): (_) => handleArchive(), + ): (_) => + handleArchive(), }, if (isOwner && asset.stackCount > 0 && !isInLockedView) { @@ -293,7 +274,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.burst_mode_outlined), label: 'stack'.tr(), tooltip: 'stack'.tr(), - ): (_) => showStackActionItems(), + ): (_) => + showStackActionItems(), }, if (isOwner && !isInAlbum) { @@ -301,7 +283,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.delete_outline), label: 'delete'.tr(), tooltip: 'delete'.tr(), - ): (_) => handleDelete(), + ): (_) => + handleDelete(), }, if (!isOwner) { @@ -309,7 +292,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.download_outlined), label: 'download'.tr(), tooltip: 'download'.tr(), - ): (_) => handleDownload(), + ): (_) => + handleDownload(), }, if (isInAlbum) { @@ -317,7 +301,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.remove_circle_outline), label: 'remove_from_album'.tr(), tooltip: 'remove_from_album'.tr(), - ): (_) => handleRemoveFromAlbum(), + ): (_) => + handleRemoveFromAlbum(), }, ]; return IgnorePointer( @@ -344,16 +329,8 @@ class BottomGalleryBar extends ConsumerWidget { backgroundColor: Colors.transparent, unselectedIconTheme: const IconThemeData(color: Colors.white), selectedIconTheme: const IconThemeData(color: Colors.white), - unselectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - selectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), + unselectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), + selectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), unselectedFontSize: 14, selectedFontSize: 14, selectedItemColor: Colors.white, diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index a0373bcb6c..4db1d9bb69 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -21,10 +21,7 @@ class CastDialog extends ConsumerWidget { } return AlertDialog( - title: const Text( - "cast", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("cast", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SizedBox( width: 250, height: 250, @@ -32,20 +29,13 @@ class CastDialog extends ConsumerWidget { future: ref.read(castProvider.notifier).getDevices(), builder: (context, snapshot) { if (snapshot.hasError) { - return Text( - 'Error: ${snapshot.error.toString()}', - ); + return Text('Error: ${snapshot.error.toString()}'); } else if (!snapshot.hasData) { - return const SizedBox( - height: 48, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 48, child: Center(child: CircularProgressIndicator())); } if (snapshot.data!.isEmpty) { - return const Text( - 'no_cast_devices_found', - ).tr(); + return const Text('no_cast_devices_found').tr(); } final devices = snapshot.data!; @@ -74,13 +64,7 @@ class CastDialog extends ConsumerWidget { // It's a section header return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - item, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ).tr(), + child: Text(item, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)).tr(), ); } else { final (deviceName, type, deviceObj) = item as (String, CastDestinationType, dynamic); @@ -88,9 +72,7 @@ class CastDialog extends ConsumerWidget { return ListTile( title: Text( deviceName, - style: TextStyle( - color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, - ), + style: TextStyle(color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null), ), leading: Icon( type == CastDestinationType.googleCast ? Icons.cast : Icons.cast_connected, @@ -99,8 +81,8 @@ class CastDialog extends ConsumerWidget { trailing: isCurrentDevice(deviceName) ? Icon(Icons.check, color: context.colorScheme.primary) : isDeviceConnecting(deviceName) - ? const CircularProgressIndicator() - : null, + ? const CircularProgressIndicator() + : null, onTap: () async { if (isDeviceConnecting(deviceName)) { return; @@ -127,20 +109,14 @@ class CastDialog extends ConsumerWidget { onPressed: () => ref.read(castProvider.notifier).disconnect(), child: Text( "stop_casting", - style: TextStyle( - color: context.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.secondary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () => context.pop(), child: Text( "close", - style: TextStyle( - color: context.colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.primary, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_viewer/center_play_button.dart b/mobile/lib/widgets/asset_viewer/center_play_button.dart index 6d7aead9d1..26d0a41129 100644 --- a/mobile/lib/widgets/asset_viewer/center_play_button.dart +++ b/mobile/lib/widgets/asset_viewer/center_play_button.dart @@ -29,19 +29,13 @@ class CenterPlayButton extends StatelessWidget { opacity: show ? 1.0 : 0.0, duration: const Duration(milliseconds: 100), child: DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle), child: IconButton( iconSize: 32, padding: const EdgeInsets.all(12.0), icon: isFinished ? Icon(Icons.replay, color: iconColor) - : AnimatedPlayPause( - color: iconColor, - playing: isPlaying, - ), + : AnimatedPlayPause(color: iconColor, playing: isPlaying), onPressed: onPressed, ), ), diff --git a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart index d70761f37d..0e766c77b9 100644 --- a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart +++ b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart @@ -13,36 +13,28 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class CustomVideoPlayerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const CustomVideoPlayerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const CustomVideoPlayerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetProvider.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetProvider.select((asset) => asset != null && asset.isVideo)); final showControls = ref.watch(showControlsProvider); final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { - ref.read(showControlsProvider.notifier).show = false; - } - }, - ); + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(showControlsProvider.notifier).show = false; + } + }); final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them @@ -93,11 +85,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( onTap: () => ref.read(showControlsProvider.notifier).show = false, diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart index 3ac60fd613..b0cefd63fa 100644 --- a/mobile/lib/widgets/asset_viewer/description_input.dart +++ b/mobile/lib/widgets/asset_viewer/description_input.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; class DescriptionInput extends HookConsumerWidget { - DescriptionInput({ - super.key, - required this.asset, - this.exifInfo, - }); + DescriptionInput({super.key, required this.asset, this.exifInfo}); final Asset asset; final ExifInfo? exifInfo; @@ -37,16 +33,13 @@ class DescriptionInput extends HookConsumerWidget { final hasDescription = useState(false); final isOwner = fastHash(owner?.id ?? '') == asset.ownerId; - useEffect( - () { - assetService.getDescription(asset).then((value) { - controller.text = value; - hasDescription.value = value.isNotEmpty; - }); - return null; - }, - [assetWithExif.value], - ); + useEffect(() { + assetService.getDescription(asset).then((value) { + controller.text = value; + hasDescription.value = value.isNotEmpty; + }); + return null; + }, [assetWithExif.value]); if (!isOwner && !hasDescription.value) { return const SizedBox.shrink(); @@ -55,19 +48,12 @@ class DescriptionInput extends HookConsumerWidget { submitDescription(String description) async { hasError.value = false; try { - await assetService.setDescription( - asset, - description, - ); + await assetService.setDescription(asset, description); controller.text = description; } catch (error, stack) { hasError.value = true; _log.severe("Error updating description", error, stack); - ImmichToast.show( - context: context, - msg: "description_input_submit_error".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "description_input_submit_error".tr(), toastType: ToastType.error); } } @@ -80,10 +66,7 @@ class DescriptionInput extends HookConsumerWidget { controller.clear(); isTextEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.colorScheme.onSurfaceSecondary, - ), + icon: Icon(Icons.cancel_rounded, color: context.colorScheme.onSurfaceSecondary), splashRadius: 10, ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart index e29da52280..df8f6593df 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart @@ -36,18 +36,8 @@ class AssetDateTime extends ConsumerWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - formattedDateTime, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - if (asset.isRemote) - IconButton( - onPressed: editDateTime, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + Text(formattedDateTime, style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600)), + if (asset.isRemote) IconButton(onPressed: editDateTime, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart index 59b52344e7..f0f9a2efcb 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart @@ -12,11 +12,7 @@ class AssetDetails extends ConsumerWidget { final Asset asset; final ExifInfo? exifInfo; - const AssetDetails({ - super.key, - required this.asset, - this.exifInfo, - }); + const AssetDetails({super.key, required this.asset, this.exifInfo}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart index 2a9e6f4a24..7ad290c152 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart @@ -11,10 +11,7 @@ import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart'; class AssetLocation extends HookConsumerWidget { final Asset asset; - const AssetLocation({ - super.key, - required this.asset, - }); + const AssetLocation({super.key, required this.asset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,10 +32,7 @@ class AssetLocation extends HookConsumerWidget { leading: const Icon(Icons.location_on), title: Text( "add_a_location", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), onTap: editLocation, ) @@ -56,10 +50,7 @@ class AssetLocation extends HookConsumerWidget { bool hasLocationName = (cityName != null && stateName != null); return hasLocationName - ? Text( - "$cityName, $stateName", - style: context.textTheme.labelLarge, - ) + ? Text("$cityName, $stateName", style: context.textTheme.labelLarge) : const SizedBox.shrink(); } @@ -79,25 +70,16 @@ class AssetLocation extends HookConsumerWidget { ), ).tr(), if (asset.isRemote) - IconButton( - onPressed: editLocation, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + IconButton(onPressed: editLocation, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ), asset.isRemote ? const SizedBox.shrink() : const SizedBox(height: 16), - ExifMap( - exifInfo: exifInfo!, - markerId: asset.remoteId, - ), + ExifMap(exifInfo: exifInfo!, markerId: asset.remoteId), const SizedBox(height: 16), getLocationName(), Text( "${exifInfo.latitude!.toStringAsFixed(4)}, ${exifInfo.longitude!.toStringAsFixed(4)}", - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart index bd859b8ced..5ae29d32c7 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class CameraInfo extends StatelessWidget { final ExifInfo exifInfo; - const CameraInfo({ - super.key, - required this.exifInfo, - }); + const CameraInfo({super.key, required this.exifInfo}); @override Widget build(BuildContext context) { @@ -16,14 +13,8 @@ class CameraInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.camera, - color: textColor.withAlpha(200), - ), - title: Text( - "${exifInfo.make} ${exifInfo.model}", - style: context.textTheme.labelLarge, - ), + leading: Icon(Icons.camera, color: textColor.withAlpha(200)), + title: Text("${exifInfo.make} ${exifInfo.model}", style: context.textTheme.labelLarge), subtitle: exifInfo.f != null || exifInfo.exposureSeconds != null || exifInfo.mm != null || exifInfo.iso != null ? Text( "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ", diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index 7b6325cf2c..04d01194e9 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -11,12 +11,7 @@ class ExifMap extends StatelessWidget { final String? markerId; final MapCreatedCallback? onMapCreated; - const ExifMap({ - super.key, - required this.exifInfo, - this.markerId = 'marker', - this.onMapCreated, - }); + const ExifMap({super.key, required this.exifInfo, this.markerId = 'marker', this.onMapCreated}); @override Widget build(BuildContext context) { @@ -35,20 +30,13 @@ class ExifMap extends StatelessWidget { Uri uri = Uri( scheme: 'geo', host: '$latitude,$longitude', - queryParameters: { - 'z': '$zoomLevel', - 'q': '$latitude,$longitude', - }, + queryParameters: {'z': '$zoomLevel', 'q': '$latitude,$longitude'}, ); if (await canLaunchUrl(uri)) { return uri; } } else if (Platform.isIOS) { - var params = { - 'll': '$latitude,$longitude', - 'q': '$latitude,$longitude', - 'z': '$zoomLevel', - }; + var params = {'ll': '$latitude,$longitude', 'q': '$latitude,$longitude', 'z': '$zoomLevel'}; Uri uri = Uri.https('maps.apple.com', '/', params); if (await canLaunchUrl(uri)) { return uri; @@ -66,10 +54,7 @@ class ExifMap extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { return MapThumbnail( - centre: LatLng( - exifInfo.latitude ?? 0, - exifInfo.longitude ?? 0, - ), + centre: LatLng(exifInfo.latitude ?? 0, exifInfo.longitude ?? 0), height: 150, width: constraints.maxWidth, zoom: 12.0, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart index 486918c436..78d9ac1776 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; class FileInfo extends StatelessWidget { final Asset asset; - const FileInfo({ - super.key, - required this.asset, - }); + const FileInfo({super.key, required this.asset}); @override Widget build(BuildContext context) { @@ -41,15 +38,9 @@ class FileInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.image, - color: textColor.withAlpha(200), - ), + leading: Icon(Icons.image, color: textColor.withAlpha(200)), titleAlignment: ListTileTitleAlignment.center, - title: Text( - title, - style: context.textTheme.labelLarge, - ), + title: Text(title, style: context.textTheme.labelLarge), subtitle: subtitle == null ? null : Text(subtitle), ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index a97a04a453..b2aa50493c 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -21,10 +21,7 @@ class PeopleInfo extends ConsumerWidget { final peopleProvider = ref.watch(assetPeopleNotifierProvider(asset).notifier); final people = ref.watch(assetPeopleNotifierProvider(asset)).value?.where((p) => !p.isHidden); - showPersonNameEditModel( - String personId, - String personName, - ) { + showPersonNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -37,7 +34,8 @@ class PeopleInfo extends ConsumerWidget { }); } - final curatedPeople = people + final curatedPeople = + people ?.map( (p) => SearchCuratedContent( id: p.id, @@ -78,17 +76,10 @@ class PeopleInfo extends ConsumerWidget { content: curatedPeople, onTap: (content, index) { context - .pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + .pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) .then((_) => peopleProvider.refresh()); }, - onNameTap: (person, index) => { - showPersonNameEditModel(person.id, person.label), - }, + onNameTap: (person, index) => {showPersonNameEditModel(person.id, person.label)}, ), ), ], diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index d18dc92575..fbcc8e6482 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -11,11 +11,7 @@ class FormattedDuration extends StatelessWidget { width: data.inHours > 0 ? 70 : 60, // use a fixed width to prevent jitter child: Text( data.format(), - style: const TextStyle( - fontSize: 14.0, - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 14.0, color: Colors.white, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ); diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart index 1c24412259..dcb0334801 100644 --- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart @@ -51,11 +51,7 @@ class GalleryAppBar extends ConsumerWidget { final result = await ref.read(trashProvider.notifier).restoreAssets([asset]); if (result && context.mounted) { - ImmichToast.show( - context: context, - msg: 'asset_restored_successfully'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'asset_restored_successfully'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -75,16 +71,10 @@ class GalleryAppBar extends ConsumerWidget { addToAlbum(Asset addToAlbumAsset) { showModalBottomSheet( elevation: 0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(15.0), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), context: context, builder: (BuildContext _) { - return AddToAlbumBottomSheet( - assets: [addToAlbumAsset], - ); + return AddToAlbumBottomSheet(assets: [addToAlbumAsset]); }, ); } diff --git a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart index 1d04115b7c..35f3840797 100644 --- a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart @@ -58,10 +58,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildFavoriteButton(a) { return IconButton( onPressed: () => onFavorite(a), - icon: Icon( - a.isFavorite ? Icons.favorite : Icons.favorite_border, - color: Colors.grey[200], - ), + icon: Icon(a.isFavorite ? Icons.favorite : Icons.favorite_border, color: Colors.grey[200]), ); } @@ -70,10 +67,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onLocatePressed(); }, - icon: Icon( - Icons.image_search, - color: Colors.grey[200], - ), + icon: Icon(Icons.image_search, color: Colors.grey[200]), ); } @@ -82,20 +76,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onMoreInfoPressed(); }, - icon: Icon( - Icons.info_outline_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.info_outline_rounded, color: Colors.grey[200]), ); } Widget buildDownloadButton() { return IconButton( onPressed: onDownloadPressed, - icon: Icon( - Icons.cloud_download_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.cloud_download_outlined, color: Colors.grey[200]), ); } @@ -104,10 +92,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onAddToAlbumPressed(); }, - icon: Icon( - Icons.add, - color: Colors.grey[200], - ), + icon: Icon(Icons.add, color: Colors.grey[200]), ); } @@ -116,10 +101,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onRestorePressed(); }, - icon: Icon( - Icons.history_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.history_rounded, color: Colors.grey[200]), ); } @@ -131,19 +113,13 @@ class TopControlAppBar extends HookConsumerWidget { icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon( - Icons.mode_comment_outlined, - color: Colors.grey[200], - ), + Icon(Icons.mode_comment_outlined, color: Colors.grey[200]), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey[200], - ), + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey[200]), ), ), ], @@ -154,10 +130,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildUploadButton() { return IconButton( onPressed: onUploadPressed, - icon: Icon( - Icons.backup_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.backup_outlined, color: Colors.grey[200]), ); } @@ -166,21 +139,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - color: Colors.grey[200], - ), + icon: Icon(Icons.arrow_back_ios_new_rounded, size: 20.0, color: Colors.grey[200]), ); } Widget buildCastButton() { return IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, icon: Icon( isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, diff --git a/mobile/lib/widgets/asset_viewer/video_controls.dart b/mobile/lib/widgets/asset_viewer/video_controls.dart index 22aa2b17d1..42f6078478 100644 --- a/mobile/lib/widgets/asset_viewer/video_controls.dart +++ b/mobile/lib/widgets/asset_viewer/video_controls.dart @@ -12,9 +12,6 @@ class VideoControls extends ConsumerWidget { final isPortrait = context.orientation == Orientation.portrait; return isPortrait ? const VideoPosition() - : const Padding( - padding: EdgeInsets.symmetric(horizontal: 60.0), - child: VideoPosition(), - ); + : const Padding(padding: EdgeInsets.symmetric(horizontal: 60.0), child: VideoPosition()); } } diff --git a/mobile/lib/widgets/asset_viewer/video_position.dart b/mobile/lib/widgets/asset_viewer/video_position.dart index 2bd2eb80bf..c12bb5e682 100644 --- a/mobile/lib/widgets/asset_viewer/video_position.dart +++ b/mobile/lib/widgets/asset_viewer/video_position.dart @@ -17,12 +17,8 @@ class VideoPosition extends HookConsumerWidget { final isCasting = ref.watch(castProvider).isCasting; final (position, duration) = isCasting - ? ref.watch( - castProvider.select((c) => (c.currentTime, c.duration)), - ) - : ref.watch( - videoPlaybackValueProvider.select((v) => (v.position, v.duration)), - ); + ? ref.watch(castProvider.select((c) => (c.currentTime, c.duration))) + : ref.watch(videoPlaybackValueProvider.select((v) => (v.position, v.duration))); final wasPlaying = useRef(true); return duration == Duration.zero @@ -34,20 +30,14 @@ class VideoPosition extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(position), - FormattedDuration(duration), - ], + children: [FormattedDuration(position), FormattedDuration(duration)], ), ), Row( children: [ Expanded( child: Slider( - value: min( - position.inMicroseconds / duration.inMicroseconds * 100, - 100, - ), + value: min(position.inMicroseconds / duration.inMicroseconds * 100, 100), min: 0, max: 100, thumbColor: Colors.white, @@ -98,10 +88,7 @@ class _VideoPositionPlaceholder extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(Duration.zero), - FormattedDuration(Duration.zero), - ], + children: [FormattedDuration(Duration.zero), FormattedDuration(Duration.zero)], ), ), Row( diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index 696168a384..d635e136bc 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { final AvailableAlbum album; - const AlbumInfoCard({ - super.key, - required this.album, - }); + const AlbumInfoCard({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,10 +26,7 @@ class AlbumInfoCard extends HookConsumerWidget { final isDarkTheme = context.isDarkTheme; - ColorFilter selectedFilter = ColorFilter.mode( - context.primaryColor.withAlpha(100), - BlendMode.darken, - ); + ColorFilter selectedFilter = ColorFilter.mode(context.primaryColor.withAlpha(100), BlendMode.darken); ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); ColorFilter unselectedFilter = const ColorFilter.mode(Colors.black, BlendMode.color); @@ -40,9 +34,7 @@ class AlbumInfoCard extends HookConsumerWidget { if (isSelected) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_included", style: TextStyle( @@ -56,9 +48,7 @@ class AlbumInfoCard extends HookConsumerWidget { } else if (isExcluded) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_excluded", style: TextStyle( @@ -145,24 +135,16 @@ class AlbumInfoCard extends HookConsumerWidget { child: const Image( width: double.infinity, height: double.infinity, - image: AssetImage( - 'assets/immich-logo.png', - ), + image: AssetImage('assets/immich-logo.png'), fit: BoxFit.cover, ), ), - Positioned( - bottom: 10, - right: 25, - child: buildSelectedTextBox(), - ), + Positioned(bottom: 10, right: 25, child: buildSelectedTextBox()), ], ), ), Padding( - padding: const EdgeInsets.only( - left: 25, - ), + padding: const EdgeInsets.only(left: 25), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -173,20 +155,13 @@ class AlbumInfoCard extends HookConsumerWidget { children: [ Text( album.name, - style: TextStyle( - fontSize: 14, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: context.primaryColor, fontWeight: FontWeight.bold), ), Padding( padding: const EdgeInsets.only(top: 2.0), child: Text( album.assetCount.toString() + (album.isAll ? " (${'all'.tr()})" : ""), - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - ), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ), ], @@ -194,15 +169,9 @@ class AlbumInfoCard extends HookConsumerWidget { ), IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ], diff --git a/mobile/lib/widgets/backup/album_info_list_tile.dart b/mobile/lib/widgets/backup/album_info_list_tile.dart index 7558b909bb..9796f45e8b 100644 --- a/mobile/lib/widgets/backup/album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/album_info_list_tile.dart @@ -35,23 +35,14 @@ class AlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -92,25 +83,13 @@ class AlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart index a87832d3f1..2cccded2bb 100644 --- a/mobile/lib/widgets/backup/asset_info_table.dart +++ b/mobile/lib/widgets/backup/asset_info_table.dart @@ -14,9 +14,7 @@ class BackupAssetInfoTable extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isUploadInProgress = ref.watch( @@ -29,18 +27,13 @@ class BackupAssetInfoTable extends ConsumerWidget { ); final asset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset), - ) + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset)) : ref.watch(backupProvider.select((value) => value.currentUploadAsset)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Table( - border: TableBorder.all( - color: context.colorScheme.outlineVariant, - width: 1, - ), + border: TableBorder.all(color: context.colorScheme.outlineVariant, width: 1), children: [ TableRow( children: [ @@ -48,21 +41,19 @@ class BackupAssetInfoTable extends ConsumerWidget { verticalAlignment: TableCellVerticalAlignment.middle, child: Padding( padding: const EdgeInsets.all(6.0), - child: Text( - 'backup_controller_page_filename', - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - fontWeight: FontWeight.bold, - fontSize: 10.0, - ), - ).tr( - namedArgs: isUploadInProgress - ? { - 'filename': asset.fileName, - 'size': asset.fileType.toLowerCase(), - } - : {'filename': "-", 'size': "-"}, - ), + child: + Text( + 'backup_controller_page_filename', + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary, + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + namedArgs: isUploadInProgress + ? {'filename': asset.fileName, 'size': asset.fileType.toLowerCase()} + : {'filename': "-", 'size': "-"}, + ), ), ), ], @@ -80,11 +71,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: { - 'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-", - }, - ), + ).tr(namedArgs: {'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-"}), ), ), ], @@ -101,9 +88,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: {'id': isUploadInProgress ? asset.id : "-"}, - ), + ).tr(namedArgs: {'id': isUploadInProgress ? asset.id : "-"}), ), ), ], diff --git a/mobile/lib/widgets/backup/backup_info_card.dart b/mobile/lib/widgets/backup/backup_info_card.dart index 54551da35a..767d94bbf9 100644 --- a/mobile/lib/widgets/backup/backup_info_card.dart +++ b/mobile/lib/widgets/backup/backup_info_card.dart @@ -7,12 +7,7 @@ class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; final String info; - const BackupInfoCard({ - super.key, - required this.title, - required this.subtitle, - required this.info, - }); + const BackupInfoCard({super.key, required this.title, required this.subtitle, required this.info}); @override Widget build(BuildContext context) { @@ -21,40 +16,26 @@ class BackupInfoCard extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(20), // if you need this ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, isThreeLine: true, - title: Text( - title, - style: context.textTheme.titleMedium, - ), + title: Text(title, style: context.textTheme.titleMedium), subtitle: Padding( padding: const EdgeInsets.only(top: 4.0, right: 18.0), child: Text( subtitle, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - info, - style: context.textTheme.titleLarge, - ), - Text( - "backup_info_card_assets", - style: context.textTheme.labelLarge, - ).tr(), + Text(info, style: context.textTheme.titleLarge), + Text("backup_info_card_assets", style: context.textTheme.labelLarge).tr(), ], ), ), diff --git a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart index b6d0edb200..c2f94e706a 100644 --- a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart +++ b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart @@ -16,18 +16,11 @@ class CurrentUploadingAssetInfoBox extends StatelessWidget { Widget build(BuildContext context) { return ListTile( isThreeLine: true, - leading: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 30, - ), + leading: Icon(Icons.image_outlined, color: context.primaryColor, size: 30), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "backup_controller_page_uploading_file_info", - style: context.textTheme.titleSmall, - ).tr(), + Text("backup_controller_page_uploading_file_info", style: context.textTheme.titleSmall).tr(), const BackupErrorChip(), ], ), diff --git a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart index 25d5bfef28..cc3485e6f9 100644 --- a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart @@ -36,23 +36,14 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -90,23 +81,13 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { context.pushRoute(LocalTimelineRoute(album: album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/error_chip.dart b/mobile/lib/widgets/backup/error_chip.dart index 3c743a0903..191049cd75 100644 --- a/mobile/lib/widgets/backup/error_chip.dart +++ b/mobile/lib/widgets/backup/error_chip.dart @@ -17,10 +17,7 @@ class BackupErrorChip extends ConsumerWidget { } return ActionChip( - avatar: const Icon( - Icons.info, - color: red400, - ), + avatar: const Icon(Icons.info, color: red400), elevation: 1, visualDensity: VisualDensity.compact, label: const BackupErrorChipText(), diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart index 38c527ccfa..c987dfd331 100644 --- a/mobile/lib/widgets/backup/error_chip_text.dart +++ b/mobile/lib/widgets/backup/error_chip_text.dart @@ -16,11 +16,7 @@ class BackupErrorChipText extends ConsumerWidget { return const Text( "backup_controller_page_failed", - style: TextStyle( - color: red400, - fontWeight: FontWeight.bold, - fontSize: 11, - ), + style: TextStyle(color: red400, fontWeight: FontWeight.bold, fontSize: 11), ).tr(namedArgs: {'count': count.toString()}); } } diff --git a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart index 7d292112ea..9f0f7ec3eb 100644 --- a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart +++ b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart @@ -10,18 +10,12 @@ class IcloudDownloadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); if (!isIcloudAsset) { return const SizedBox(); @@ -33,13 +27,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - SizedBox( - width: 110, - child: Text( - "iCloud Download", - style: context.textTheme.labelSmall, - ), - ), + SizedBox(width: 110, child: Text("iCloud Download", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, @@ -47,10 +35,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), - Text( - " ${iCloudDownloadProgress ~/ 1}%", - style: const TextStyle(fontSize: 12), - ), + Text(" ${iCloudDownloadProgress ~/ 1}%", style: const TextStyle(fontSize: 12)), ], ), ); diff --git a/mobile/lib/widgets/backup/ios_debug_info_tile.dart b/mobile/lib/widgets/backup/ios_debug_info_tile.dart index 0f96e4cef6..be333c6460 100644 --- a/mobile/lib/widgets/backup/ios_debug_info_tile.dart +++ b/mobile/lib/widgets/backup/ios_debug_info_tile.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/providers/backup/ios_background_settings.provider. /// more confident about background sync class IosDebugInfoTile extends HookConsumerWidget { final IOSBackgroundSettings settings; - const IosDebugInfoTile({ - super.key, - required this.settings, - }); + const IosDebugInfoTile({super.key, required this.settings}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -37,31 +34,16 @@ class IosDebugInfoTile extends HookConsumerWidget { subtitle = 'ios_debug_info_processing_ran_at'.t(context: context, args: {'dateTime': df.format(processing)}); } else { final fetchOrProcessing = fetch!.isAfter(processing!) ? fetch : processing; - subtitle = 'ios_debug_info_last_sync_at'.t( - context: context, - args: {'dateTime': df.format(fetchOrProcessing)}, - ); + subtitle = 'ios_debug_info_last_sync_at'.t(context: context, args: {'dateTime': df.format(fetchOrProcessing)}); } return ListTile( title: Text( title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: const TextStyle( - fontSize: 14, - ), - ), - leading: Icon( - Icons.bug_report, - color: context.primaryColor, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: context.primaryColor), ), + subtitle: Text(subtitle, style: const TextStyle(fontSize: 14)), + leading: Icon(Icons.bug_report, color: context.primaryColor), ); } } diff --git a/mobile/lib/widgets/backup/upload_progress_bar.dart b/mobile/lib/widgets/backup/upload_progress_bar.dart index b11a8562b9..65ff6c758a 100644 --- a/mobile/lib/widgets/backup/upload_progress_bar.dart +++ b/mobile/lib/widgets/backup/upload_progress_bar.dart @@ -11,39 +11,22 @@ class BackupUploadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); final uploadProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInPercentage), - ) - : ref.watch( - backupProvider.select((value) => value.progressInPercentage), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInPercentage)) + : ref.watch(backupProvider.select((value) => value.progressInPercentage)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - if (isIcloudAsset) - SizedBox( - width: 110, - child: Text( - "Immich Upload", - style: context.textTheme.labelSmall, - ), - ), + if (isIcloudAsset) SizedBox(width: 110, child: Text("Immich Upload", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, diff --git a/mobile/lib/widgets/backup/upload_stats.dart b/mobile/lib/widgets/backup/upload_stats.dart index 965202ce33..c9b626c51c 100644 --- a/mobile/lib/widgets/backup/upload_stats.dart +++ b/mobile/lib/widgets/backup/upload_stats.dart @@ -10,34 +10,23 @@ class BackupUploadStats extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final uploadFileProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSize), - ) + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSize)) : ref.watch(backupProvider.select((value) => value.progressInFileSize)); final uploadFileSpeed = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSpeed), - ) - : ref.watch( - backupProvider.select((value) => value.progressInFileSpeed), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSpeed)) + : ref.watch(backupProvider.select((value) => value.progressInFileSpeed)); return Padding( padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - uploadFileProgress, - style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), - ), + Text(uploadFileProgress, style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono")), Text( _formatUploadFileSpeed(uploadFileSpeed), style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index e0fa03ba01..ccfc374fef 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -34,27 +34,18 @@ class ImmichAppBarDialog extends HookConsumerWidget { final user = ref.watch(currentUserProvider); final isLoggingOut = useState(false); - useEffect( - () { - ref.read(backupProvider.notifier).updateDiskInfo(); - ref.read(currentUserProvider.notifier).refresh(); - return null; - }, - [], - ); + useEffect(() { + ref.read(backupProvider.notifier).updateDiskInfo(); + ref.read(currentUserProvider.notifier).refresh(); + return null; + }, []); buildTopRow() { return Stack( children: [ Align( alignment: Alignment.topLeft, - child: InkWell( - onTap: () => context.pop(), - child: const Icon( - Icons.close, - size: 20, - ), - ), + child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)), ), Center( child: Image.asset( @@ -66,29 +57,16 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } - buildActionButton( - IconData icon, - String text, - Function() onTap, { - Widget? trailing, - }) { + buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) { return ListTile( dense: true, visualDensity: VisualDensity.standard, contentPadding: const EdgeInsets.only(left: 30, right: 30), minLeadingWidth: 40, - leading: SizedBox( - child: Icon( - icon, - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - size: 20, - ), - ), + leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)), title: Text( text, - style: theme.textTheme.labelLarge?.copyWith( - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - ), + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), ).tr(), onTap: onTap, trailing: trailing, @@ -96,11 +74,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { } buildSettingButton() { - return buildActionButton( - Icons.settings_outlined, - "settings", - () => context.pushRoute(const SettingsRoute()), - ); + return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute())); } buildAppLogButton() { @@ -142,10 +116,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); }, trailing: isLoggingOut.value - ? const SizedBox.square( - dimension: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) + ? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2)) : null, ); } @@ -165,20 +136,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3), child: Container( padding: const EdgeInsets.symmetric(vertical: 4), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: ListTile( minLeadingWidth: 50, - leading: Icon( - Icons.storage_rounded, - color: theme.primaryColor, - ), + leading: Icon(Icons.storage_rounded, color: theme.primaryColor), title: Text( "backup_controller_page_server_storage", - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(), isThreeLine: true, subtitle: Padding( @@ -196,12 +160,9 @@ class ImmichAppBarDialog extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 12.0), - child: const Text('backup_controller_page_storage_format').tr( - namedArgs: { - 'used': usedDiskSpace, - 'total': totalDiskSpace, - }, - ), + child: const Text( + 'backup_controller_page_storage_format', + ).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}), ), ], ), @@ -220,43 +181,19 @@ class ImmichAppBarDialog extends HookConsumerWidget { InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://immich.app'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication); }, - child: Text( - "documentation", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("documentation", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://github.com/immich-app/immich'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication); }, - child: Text( - "profile_drawer_github", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () async { context.pop(); @@ -291,20 +228,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { right: horizontalPadding, bottom: isHorizontal ? 20 : 100, ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), child: SizedBox( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - Container( - padding: const EdgeInsets.all(20), - child: buildTopRow(), - ), + Container(padding: const EdgeInsets.all(20), child: buildTopRow()), const AppBarProfileInfoBox(), buildStorageInformation(), const AppBarServerInfo(), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 080c17e951..d12ee7b0d1 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -10,9 +10,7 @@ import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AppBarProfileInfoBox extends HookConsumerWidget { - const AppBarProfileInfoBox({ - super.key, - }); + const AppBarProfileInfoBox({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,38 +27,24 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ); } - final userImage = UserCircleAvatar( - radius: 22, - size: 44, - user: user, - ); + final userImage = UserCircleAvatar(radius: 22, size: 44, user: user); if (uploadProfileImageStatus == UploadProfileStatus.loading) { - return const SizedBox( - height: 40, - width: 40, - child: ImmichLoadingIndicator(borderRadius: 20), - ); + return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20)); } return userImage; } pickUserProfileImage() async { - final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, - maxHeight: 1024, - maxWidth: 1024, - ); + final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); if (image != null) { var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image); if (success) { final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath; - ref.watch(authProvider.notifier).updateUserProfileImagePath( - profileImagePath, - ); + ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath); if (user != null) { ref.read(currentUserProvider.notifier).refresh(); } @@ -74,10 +58,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget { width: double.infinity, decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), ), child: ListTile( minLeadingWidth: 50, @@ -93,16 +74,10 @@ class AppBarProfileInfoBox extends HookConsumerWidget { child: Material( color: context.colorScheme.surfaceContainerHighest, elevation: 3, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(50.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), child: Padding( padding: const EdgeInsets.all(5.0), - child: Icon( - Icons.camera_alt_outlined, - color: context.primaryColor, - size: 14, - ), + child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), ), ), ), @@ -111,16 +86,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ), title: Text( authState.name, - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500), ), subtitle: Text( authState.userEmail, - style: context.textTheme.bodySmall?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart index 6b990c1c08..4aacfb3322 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:package_info_plus/package_info_plus.dart'; class AppBarServerInfo extends HookConsumerWidget { - const AppBarServerInfo({ - super.key, - }); + const AppBarServerInfo({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget { getPackageInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - appInfo.value = { - "version": packageInfo.version, - "buildNumber": packageInfo.buildNumber, - }; + appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber}; } - useEffect( - () { - getPackageInfo(); - return null; - }, - [], - ); + useEffect(() { + getPackageInfo(); + return null; + }, []); return Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), child: Container( decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), @@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget { ? serverInfoState.versionMismatchErrorMessage : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500), ), ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -197,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -212,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget { if (serverInfoState.isNewReleaseAvailable) const Padding( padding: EdgeInsets.only(right: 5.0), - child: Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: 12, - ), + child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12), ), Text( "latest_version".tr(), diff --git a/mobile/lib/widgets/common/confirm_dialog.dart b/mobile/lib/widgets/common/confirm_dialog.dart index 411770e78f..153c124595 100644 --- a/mobile/lib/widgets/common/confirm_dialog.dart +++ b/mobile/lib/widgets/common/confirm_dialog.dart @@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: Text(title).tr(), content: Text(content).tr(), actions: [ @@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget { onPressed: () => context.pop(false), child: Text( cancel, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onOkPressed, child: Text( ok, - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 92d0b48684..113462c6c8 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -16,11 +16,8 @@ Future showDateTimePicker({ }) { return showDialog( context: context, - builder: (context) => _DateTimePicker( - initialDateTime: initialDateTime, - initialTZ: initialTZ, - initialTZOffset: initialTZOffset, - ), + builder: (context) => + _DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset), ); } @@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget { final String? initialTZ; final Duration? initialTZOffset; - const _DateTimePicker({ - this.initialDateTime, - this.initialTZ, - this.initialTZOffset, - }); + const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset}); _TimeZoneOffset _getInitiationLocation() { if (initialTZ != null) { try { - return _TimeZoneOffset.fromLocation( - tz.timeZoneDatabase.get(initialTZ!), - ); + return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!)); } on LocationNotFoundException { // no-op } @@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget { (location) => location.currentTimeZone.offset == offsetInMilli, ); // Prefer locations with abbreviation first - final location = locations.firstWhereOrNull( - (e) => !e.currentTimeZone.abbreviation.contains("0"), - ) ?? - locations.firstOrNull; + final location = + locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull; if (location != null) { return _TimeZoneOffset.fromLocation(location); } @@ -86,11 +75,7 @@ class _DateTimePicker extends HookWidget { (timezone) => DropdownMenuEntry<_TimeZoneOffset>( value: timezone, label: timezone.display, - style: ButtonStyle( - textStyle: WidgetStatePropertyAll( - context.textTheme.bodyMedium, - ), - ), + style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)), ), ) .toList(); @@ -109,10 +94,7 @@ class _DateTimePicker extends HookWidget { return; } - final newTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.fromDateTime(date.value), - ); + final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value)); if (newTime == null) { return; @@ -145,10 +127,7 @@ class _DateTimePicker extends HookWidget { onPressed: popWithDateTime, child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -156,46 +135,22 @@ class _DateTimePicker extends HookWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - "date_and_time", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ).tr(), + const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(), const SizedBox(height: 32), ListTile( tileColor: context.colorScheme.surfaceContainerHighest, shape: ShapeBorder.lerp( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), 1, ), - trailing: Icon( - Icons.edit_outlined, - size: 18, - color: context.primaryColor, - ), - title: Text( - DateFormat("dd-MM-yyyy hh:mm a").format(date.value), - style: context.textTheme.bodyMedium, - ).tr(), + trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(), onTap: pickDate, ), const SizedBox(height: 24), DropdownSearchMenu( - trailingIcon: Icon( - Icons.arrow_drop_down, - color: context.primaryColor, - ), + trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor), hintText: "timezone".tr(), label: const Text('timezone').tr(), textStyle: context.textTheme.bodyMedium, @@ -213,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { final String display; final Location location; - const _TimeZoneOffset({ - required this.display, - required this.location, - }); + const _TimeZoneOffset({required this.display, required this.location}); - _TimeZoneOffset copyWith({ - String? display, - Location? location, - }) { - return _TimeZoneOffset( - display: display ?? this.display, - location: location ?? this.location, - ); + _TimeZoneOffset copyWith({String? display, Location? location}) { + return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location); } int get offsetInMilliseconds => location.currentTimeZone.offset; _TimeZoneOffset.fromLocation(tz.Location l) - : display = _getFormattedOffset(l.currentTimeZone.offset, l), - location = l; + : display = _getFormattedOffset(l.currentTimeZone.offset, l), + location = l; @override int compareTo(_TimeZoneOffset other) { diff --git a/mobile/lib/widgets/common/delayed_loading_indicator.dart b/mobile/lib/widgets/common/delayed_loading_indicator.dart index e54762bb9f..5fad10530e 100644 --- a/mobile/lib/widgets/common/delayed_loading_indicator.dart +++ b/mobile/lib/widgets/common/delayed_loading_indicator.dart @@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget { /// An optional fade in duration to animate the loading final Duration? fadeInDuration; - const DelayedLoadingIndicator({ - super.key, - this.delay = const Duration(seconds: 3), - this.child, - this.fadeInDuration, - }); + const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration}); @override Widget build(BuildContext context) { @@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget { builder: (context, snapshot) { late Widget c; if (snapshot.connectionState == ConnectionState.done) { - c = child ?? - const ImmichLoadingIndicator( - key: ValueKey('loading'), - ); + c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading')); } else { c = Container(key: const ValueKey('hiding')); } - return AnimatedSwitcher( - duration: fadeInDuration ?? Duration.zero, - child: c, - ); + return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c); }, ); } diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index 0e4651d819..5d1fda1beb 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget { } class ControlBoxButton extends StatelessWidget { - const ControlBoxButton({ - super.key, - required this.label, - required this.iconData, - this.onPressed, - this.onLongPressed, - }); + const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed}); final String label; final IconData iconData; @@ -37,9 +31,7 @@ class ControlBoxButton extends StatelessWidget { return MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), onPressed: onPressed, onLongPress: onLongPressed, minWidth: minWidth, diff --git a/mobile/lib/widgets/common/dropdown_search_menu.dart b/mobile/lib/widgets/common/dropdown_search_menu.dart index cde60b2afb..bf0c75c8aa 100644 --- a/mobile/lib/widgets/common/dropdown_search_menu.dart +++ b/mobile/lib/widgets/common/dropdown_search_menu.dart @@ -34,13 +34,8 @@ class DropdownSearchMenu extends HookWidget { ); final showTimeZoneDropdown = useState(false); - final effectiveConstraints = menuConstraints ?? - const BoxConstraints( - minWidth: 280, - maxWidth: 280, - minHeight: 0, - maxHeight: 280, - ); + final effectiveConstraints = + menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280); final inputDecoration = InputDecoration( contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4), @@ -58,12 +53,7 @@ class DropdownSearchMenu extends HookWidget { child: InputDecorator( decoration: inputDecoration, child: selectedItem.value != null - ? Text( - selectedItem.value!.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textStyle, - ) + ? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle) : null, ), ), @@ -89,9 +79,7 @@ class DropdownSearchMenu extends HookWidget { autofocus: true, focusNode: focusNode, controller: textEditingController, - decoration: inputDecoration.copyWith( - hintText: "search_timezone".tr(), - ), + decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()), maxLines: 1, style: context.textTheme.bodyMedium, expands: false, @@ -125,23 +113,14 @@ class DropdownSearchMenu extends HookWidget { builder: (BuildContext context) { final bool highlight = AutocompleteHighlightedOption.of(context) == index; if (highlight) { - SchedulerBinding.instance.addPostFrameCallback( - (Duration timeStamp) { - Scrollable.ensureVisible( - context, - alignment: 0.5, - ); - }, - debugLabel: 'AutocompleteOptions.ensureVisible', - ); + SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { + Scrollable.ensureVisible(context, alignment: 0.5); + }, debugLabel: 'AutocompleteOptions.ensureVisible'); } return Container( color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null, padding: const EdgeInsets.all(16.0), - child: Text( - option.label, - style: textStyle, - ), + child: Text(option.label, style: textStyle), ); }, ), diff --git a/mobile/lib/widgets/common/fade_in_placeholder_image.dart b/mobile/lib/widgets/common/fade_in_placeholder_image.dart index 2be32fa8ba..2461dbe6bf 100644 --- a/mobile/lib/widgets/common/fade_in_placeholder_image.dart +++ b/mobile/lib/widgets/common/fade_in_placeholder_image.dart @@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget { fit: StackFit.expand, children: [ placeholder, - FadeInImage( - fadeInDuration: duration, - image: image, - fit: fit, - placeholder: MemoryImage(kTransparentImage), - ), + FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)), ], ), ); diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 0d77e02aa5..7eaedd27b5 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -36,23 +36,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { buildProfileIndicator() { return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => + showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, @@ -60,17 +50,10 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -124,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -135,22 +116,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } return AppBar( backgroundColor: context.themeData.appBarTheme.backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: Builder( @@ -176,12 +149,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { ), actions: [ if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), @@ -192,25 +160,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), - if (showUploadButton) - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildBackupIndicator(), - ), - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildProfileIndicator(), - ), + if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()), + Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()), ], ); } diff --git a/mobile/lib/widgets/common/immich_image.dart b/mobile/lib/widgets/common/immich_image.dart index f51748edd6..c8bc9c1f6a 100644 --- a/mobile/lib/widgets/common/immich_image.dart +++ b/mobile/lib/widgets/common/immich_image.dart @@ -28,32 +28,19 @@ class ImmichImage extends StatelessWidget { // either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - double width = 1080, - double height = 1920, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteImageProvider( - assetId: assetId!, - ); + return ImmichRemoteImageProvider(assetId: assetId!); } if (useLocal(asset)) { - return ImmichLocalImageProvider( - asset: asset, - width: width, - height: height, - ); + return ImmichLocalImageProvider(asset: asset, width: width, height: height); } else { - return ImmichRemoteImageProvider( - assetId: asset.remoteId!, - ); + return ImmichRemoteImageProvider(assetId: asset.remoteId!); } } @@ -68,17 +55,11 @@ class ImmichImage extends StatelessWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final imageProviderInstance = ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ); + final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height); return OctoImage( fadeInDuration: const Duration(milliseconds: 0), @@ -96,11 +77,7 @@ class ImmichImage extends StatelessWidget { errorBuilder: (context, error, stackTrace) { imageProviderInstance.evict(); - return Icon( - Icons.image_not_supported_outlined, - size: 32, - color: Colors.red[200], - ); + return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]); }, ); } diff --git a/mobile/lib/widgets/common/immich_loading_indicator.dart b/mobile/lib/widgets/common/immich_loading_indicator.dart index 8f9eaeaa99..52f957f7e7 100644 --- a/mobile/lib/widgets/common/immich_loading_indicator.dart +++ b/mobile/lib/widgets/common/immich_loading_indicator.dart @@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart'; class ImmichLoadingIndicator extends HookWidget { final double? borderRadius; - const ImmichLoadingIndicator({ - super.key, - this.borderRadius, - }); + const ImmichLoadingIndicator({super.key, this.borderRadius}); @override Widget build(BuildContext context) { - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - ) + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6)) ..reverse() ..repeat(); - final borderAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - )..repeat(); + final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat(); return Container( height: 80, @@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget { animation: borderAnimationController, builder: (context, child) { return CustomPaint( - painter: GradientBorderPainter( - animation: borderAnimationController.value, - strokeWidth: 3, - ), + painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3), child: child, ); }, @@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget { padding: const EdgeInsets.all(15), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), ), @@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter { const Color(0xFF18C249), ]; - GradientBorderPainter({ - required this.animation, - required this.strokeWidth, - }); + GradientBorderPainter({required this.animation, required this.strokeWidth}); @override void paint(Canvas canvas, Size size) { @@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter { colors.first.withValues(alpha: opacity), ], // Add evenly distributed stops - stops: List.generate( - colors.length + 1, - (index) => index / colors.length, - ), + stops: List.generate(colors.length + 1, (index) => index / colors.length), tileMode: TileMode.clamp, // Use transformations to rotate the gradient transform: GradientRotation(-animation * 2 * 3.14159), diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 43987878cb..a369275282 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget { final double size; final dynamic heroTag; - const ImmichLogo({ - super.key, - this.size = 100, - this.heroTag, - }); + const ImmichLogo({super.key, this.size = 100, this.heroTag}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 18f29b9f9d..06a97d1ce5 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -52,11 +52,7 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: pinned, snap: snap, expandedHeight: expandedHeight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: title ?? const _ImmichLogoWithText(), @@ -66,38 +62,21 @@ class ImmichSliverAppBar extends ConsumerWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), const _SyncStatusIndicator(), if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), ), - if (showUploadButton) - const Padding( - padding: EdgeInsets.only(right: 20), - child: _BackupIndicator(), - ), - const Padding( - padding: EdgeInsets.only(right: 20), - child: _ProfileIndicator(), - ), + if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), + const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()), ], ), ); @@ -159,23 +138,12 @@ class _ProfileIndicator extends ConsumerWidget { const widgetSize = 30.0; return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, @@ -183,17 +151,10 @@ class _ProfileIndicator extends ConsumerWidget { serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -218,9 +179,7 @@ class _BackupIndicator extends ConsumerWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -229,11 +188,7 @@ class _BackupIndicator extends ConsumerWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } @@ -263,8 +218,9 @@ class _BackupIndicator extends ConsumerWidget { return Container( padding: const EdgeInsets.all(3.5), child: Theme( - data: context.themeData - .copyWith(progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true)), + data: context.themeData.copyWith( + progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), + ), child: CircularProgressIndicator( strokeWidth: 2, strokeCap: StrokeCap.round, @@ -302,27 +258,13 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with @override void initState() { super.initState(); - _rotationController = AnimationController( - duration: const Duration(seconds: 2), - vsync: this, - ); - _dismissalController = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); - _rotationAnimation = Tween( - begin: 0.0, - end: 1.0, - ).animate(_rotationController); + _rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); + _dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this); + _rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(_rotationController); _dismissalAnimation = Tween( begin: 1.0, end: 0.0, - ).animate( - CurvedAnimation( - parent: _dismissalController, - curve: Curves.easeOutQuart, - ), - ); + ).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart)); } @override @@ -366,11 +308,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with opacity: isSyncing ? 1.0 : _dismissalAnimation.value, child: Transform.rotate( angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise - child: Icon( - Icons.sync, - size: 24, - color: context.primaryColor, - ), + child: Icon(Icons.sync, size: 24, color: context.primaryColor), ), ), ), diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 5e3bb610d8..612a6a4bd0 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart'; import 'package:immich_mobile/providers/user.provider.dart'; class ImmichThumbnail extends HookConsumerWidget { - const ImmichThumbnail({ - this.asset, - this.width = 250, - this.height = 250, - this.fit = BoxFit.cover, - super.key, - }); + const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key}); final Asset? asset; final double width; @@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget { /// either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - String? userId, - int thumbnailSize = 256, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteThumbnailProvider( - assetId: assetId!, - ); + return ImmichRemoteThumbnailProvider(assetId: assetId!); } if (ImmichImage.useLocal(asset)) { - return ImmichLocalThumbnailProvider( - asset: asset, - height: thumbnailSize, - width: thumbnailSize, - userId: userId, - ); + return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId); } else { - return ImmichRemoteThumbnailProvider( - assetId: asset.remoteId!, - height: thumbnailSize, - width: thumbnailSize, - ); + return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize); } } @@ -72,23 +50,13 @@ class ImmichThumbnail extends HookConsumerWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final assetAltText = getAltText( - asset!.exifInfo, - asset!.fileCreatedAt, - asset!.type, - [], - ); + final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []); - final thumbnailProviderInstance = ImmichThumbnail.imageProvider( - asset: asset, - userId: userId, - ); + final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId); customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) { thumbnailProviderInstance.evict(); diff --git a/mobile/lib/widgets/common/immich_title_text.dart b/mobile/lib/widgets/common/immich_title_text.dart index 456ecdc9cf..3a848a1db6 100644 --- a/mobile/lib/widgets/common/immich_title_text.dart +++ b/mobile/lib/widgets/common/immich_title_text.dart @@ -5,18 +5,12 @@ class ImmichTitleText extends StatelessWidget { final double fontSize; final Color? color; - const ImmichTitleText({ - super.key, - this.fontSize = 48, - this.color, - }); + const ImmichTitleText({super.key, this.fontSize = 48, this.color}); @override Widget build(BuildContext context) { return Image( - image: AssetImage( - context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png', - ), + image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'), width: fontSize * 4, filterQuality: FilterQuality.high, color: context.primaryColor, diff --git a/mobile/lib/widgets/common/immich_toast.dart b/mobile/lib/widgets/common/immich_toast.dart index 945568a74c..dad8b33283 100644 --- a/mobile/lib/widgets/common/immich_toast.dart +++ b/mobile/lib/widgets/common/immich_toast.dart @@ -16,25 +16,16 @@ class ImmichToast { fToast.init(context); Color getColor(ToastType type, BuildContext context) => switch (type) { - ToastType.info => context.primaryColor, - ToastType.success => const Color.fromARGB(255, 78, 140, 124), - ToastType.error => const Color.fromARGB(255, 220, 48, 85), - }; + ToastType.info => context.primaryColor, + ToastType.success => const Color.fromARGB(255, 78, 140, 124), + ToastType.error => const Color.fromARGB(255, 220, 48, 85), + }; Icon getIcon(ToastType type) => switch (type) { - ToastType.info => Icon( - Icons.info_outline_rounded, - color: context.primaryColor, - ), - ToastType.success => const Icon( - Icons.check_circle_rounded, - color: Color.fromARGB(255, 78, 140, 124), - ), - ToastType.error => const Icon( - Icons.error_outline_rounded, - color: Color.fromARGB(255, 240, 162, 156), - ), - }; + ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor), + ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)), + ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)), + }; fToast.showToast( child: Container( @@ -42,26 +33,17 @@ class ImmichToast { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16.0)), color: context.colorScheme.surfaceContainer, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .5), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ getIcon(toastType), - const SizedBox( - width: 12.0, - ), + const SizedBox(width: 12.0), Flexible( child: Text( msg, - style: TextStyle( - color: getColor(toastType, context), - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14), ), ), ], diff --git a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart index 4880865e66..85d8f4bb01 100644 --- a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart @@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: true, centerTitle: true, - title: Text( - "on_this_device".t(context: context), - ), + title: Text("on_this_device".t(context: context)), ); } } diff --git a/mobile/lib/widgets/common/location_picker.dart b/mobile/lib/widgets/common/location_picker.dart index 81f8440836..1f63299dd7 100644 --- a/mobile/lib/widgets/common/location_picker.dart +++ b/mobile/lib/widgets/common/location_picker.dart @@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -Future showLocationPicker({ - required BuildContext context, - LatLng? initialLatLng, -}) { +Future showLocationPicker({required BuildContext context, LatLng? initialLatLng}) { return showDialog( context: context, useRootNavigator: false, - builder: (ctx) => _LocationPicker( - initialLatLng: initialLatLng, - ), + builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng), ); } @@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual } class _LocationPicker extends HookWidget { final LatLng? initialLatLng; - const _LocationPicker({ - this.initialLatLng, - }); + const _LocationPicker({this.initialLatLng}); @override Widget build(BuildContext context) { @@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget { final pickerMode = useState(_LocationPickerMode.map); Future onMapTap() async { - final newLatLng = await context.pushRoute( - MapLocationPickerRoute(initialLatLng: latlng), - ); + final newLatLng = await context.pushRoute(MapLocationPickerRoute(initialLatLng: latlng)); if (newLatLng != null) { latitude.value = newLatLng.latitude; longitude.value = newLatLng.longitude; @@ -81,10 +72,7 @@ class _LocationPicker extends HookWidget { onPressed: () => context.maybePop(latlng), child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -129,10 +117,7 @@ class _ManualPickerInput extends HookWidget { autofocus: false, decoration: InputDecoration( labelText: decorationText.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), floatingLabelBehavior: FloatingLabelBehavior.auto, border: const OutlineInputBorder(), hintText: hintText.tr(), @@ -188,10 +173,7 @@ class _ManualPicker extends HookWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( icon: const Text("location_picker_choose_on_map").tr(), @@ -228,27 +210,17 @@ class _MapPicker extends StatelessWidget { final Function() onModeSwitch; final Function() onMapTap; - const _MapPicker({ - required this.latlng, - required this.onModeSwitch, - required this.onMapTap, - super.key, - }); + const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( - icon: Text( - "${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}", - ), + icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"), label: const Icon(Icons.edit_outlined, size: 16), onPressed: onModeSwitch, ), diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index 2130a07866..359b400456 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class MesmerizingSliverAppBar extends ConsumerStatefulWidget { - const MesmerizingSliverAppBar({ - super.key, - required this.title, - this.icon = Icons.camera, - }); + const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera}); final String title; final IconData icon; @@ -62,23 +58,11 @@ class _MesmerizingSliverAppBarState extends ConsumerState 0.95 ? Text( widget.title, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -131,11 +111,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final String title; final IconData icon; - const _ExpandedBackground({ - required this.scrollProgress, - required this.title, - required this.icon, - }); + const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon}); @override ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); @@ -149,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -188,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), Container( @@ -202,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S colors: [ Colors.transparent, Colors.transparent, - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.2), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)), ], stops: const [0.0, 0.65, 1.0], ), @@ -232,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], ), ), ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), ), @@ -280,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( // letterSpacing: 0.2, fontWeight: FontWeight.bold, color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 1), - blurRadius: 6, - color: Colors.black45, - ), - ], + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], ), ); } @@ -309,10 +252,7 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); @@ -332,50 +272,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -465,9 +381,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -499,11 +413,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -530,11 +440,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 6f26c87da7..2efe8a3ce1 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -63,25 +63,13 @@ class _MesmerizingSliverAppBarState extends ConsumerState actionIconShadows = [ if (_scrollProgress < 0.95) - Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) else - const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ]; return isMultiSelectEnabled @@ -111,20 +99,12 @@ class _MesmerizingSliverAppBarState extends ConsumerState 0.95 ? Text( currentAlbum.name, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -174,11 +150,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final IconData icon; final void Function()? onEditTitle; - const _ExpandedBackground({ - required this.scrollProgress, - required this.icon, - this.onEditTitle, - }); + const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle}); @override ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); @@ -192,20 +164,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -229,9 +193,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S return const SizedBox.shrink(); } - final dateRange = ref.watch( - remoteAlbumDateRangeProvider(currentAlbum.id), - ); + final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id)); return Stack( fit: StackFit.expand, children: [ @@ -239,18 +201,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), ClipRect( child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: widget.scrollProgress * 2.0, - sigmaY: widget.scrollProgress * 2.0, - ), + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), child: Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -260,9 +216,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S Colors.black.withValues(alpha: 0.05), Colors.transparent, Colors.black.withValues(alpha: 0.3), - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.25), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), ], stops: const [0.0, 0.15, 0.55, 1.0], ), @@ -291,32 +245,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S ), style: const TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), const Text( " • ", style: TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), GestureDetector( @@ -333,13 +272,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)], ), ), ), @@ -349,31 +282,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S GestureDetector( onTap: widget.onEditTitle, child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 80, - ), + constraints: const BoxConstraints(maxHeight: 80), child: SingleChildScrollView( child: Text( currentAlbum.description, style: const TextStyle( color: Colors.white, fontSize: 14, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 8, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)], ), ), ), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0), - child: RemoteAlbumSharedUserIcons(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()), ], ), ), @@ -407,24 +329,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ); } @@ -434,10 +345,7 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); @@ -457,50 +365,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -590,9 +474,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -624,11 +506,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -655,11 +533,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/scaffold_error_body.dart b/mobile/lib/widgets/common/scaffold_error_body.dart index f1d7685f73..2e2d8fb506 100644 --- a/mobile/lib/widgets/common/scaffold_error_body.dart +++ b/mobile/lib/widgets/common/scaffold_error_body.dart @@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "scaffold_body_error_occurred", - style: context.textTheme.displayMedium, - textAlign: TextAlign.center, - ).tr(), + Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(), if (withIcon) Center( child: Padding( @@ -34,11 +30,7 @@ class ScaffoldErrorBody extends StatelessWidget { if (withIcon && errorMsg != null) Padding( padding: const EdgeInsets.all(20), - child: Text( - errorMsg!, - style: context.textTheme.displaySmall, - textAlign: TextAlign.center, - ), + child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center), ), ], ); diff --git a/mobile/lib/widgets/common/search_field.dart b/mobile/lib/widgets/common/search_field.dart index 97ac75a63b..84af2d050c 100644 --- a/mobile/lib/widgets/common/search_field.dart +++ b/mobile/lib/widgets/common/search_field.dart @@ -43,40 +43,22 @@ class SearchField extends StatelessWidget { contentPadding: contentPadding, filled: filled, fillColor: context.primaryColor.withValues(alpha: 0.1), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurfaceSecondary, - ), + hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary), border: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainer, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceContainer), ), disabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), focusedBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.primary.withAlpha(100), - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)), ), prefixIcon: prefixIcon, suffixIcon: suffixIcon, diff --git a/mobile/lib/widgets/common/selection_sliver_app_bar.dart b/mobile/lib/widgets/common/selection_sliver_app_bar.dart index 4a6dbbf385..780062e50e 100644 --- a/mobile/lib/widgets/common/selection_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/selection_sliver_app_bar.dart @@ -7,9 +7,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class SelectionSliverAppBar extends ConsumerStatefulWidget { - const SelectionSliverAppBar({ - super.key, - }); + const SelectionSliverAppBar({super.key}); @override ConsumerState createState() => _SelectionSliverAppBarState(); @@ -18,13 +16,9 @@ class SelectionSliverAppBar extends ConsumerStatefulWidget { class _SelectionSliverAppBarState extends ConsumerState { @override Widget build(BuildContext context) { - final selection = ref.watch( - multiSelectProvider.select((s) => s.selectedAssets), - ); + final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - final toExclude = ref.watch( - multiSelectProvider.select((s) => s.lockedSelectionAssets), - ); + final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets)); final filteredAssets = selection.where((asset) { return !toExclude.contains(asset); @@ -40,9 +34,7 @@ class _SelectionSliverAppBarState extends ConsumerState { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, leading: IconButton( icon: const Icon(Icons.close_rounded), @@ -52,22 +44,13 @@ class _SelectionSliverAppBarState extends ConsumerState { }, ), centerTitle: true, - title: Text( - "Select {count}".t( - context: context, - args: { - 'count': filteredAssets.length.toString(), - }, - ), - ), + title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})), actions: [ TextButton( onPressed: () => onDone(filteredAssets), child: Text( 'done'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), ), ], diff --git a/mobile/lib/widgets/common/share_dialog.dart b/mobile/lib/widgets/common/share_dialog.dart index 1c7eb3580a..625390c4b7 100644 --- a/mobile/lib/widgets/common/share_dialog.dart +++ b/mobile/lib/widgets/common/share_dialog.dart @@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), - Container( - margin: const EdgeInsets.only(top: 12), - child: const Text('share_dialog_preparing').tr(), - ), + Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()), ], ), ); diff --git a/mobile/lib/widgets/common/thumbhash_placeholder.dart b/mobile/lib/widgets/common/thumbhash_placeholder.dart index f73aa869f7..0cb1222989 100644 --- a/mobile/lib/widgets/common/thumbhash_placeholder.dart +++ b/mobile/lib/widgets/common/thumbhash_placeholder.dart @@ -6,21 +6,14 @@ import 'package:octo_image/octo_image.dart'; /// Simple set to show [OctoPlaceholder.circularProgressIndicator] as /// placeholder and [OctoError.icon] as error. -OctoSet blurHashOrPlaceholder( - Uint8List? blurhash, { - BoxFit? fit, - Text? errorMessage, -}) { +OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) { return OctoSet( placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), ); } -OctoPlaceholderBuilder blurHashPlaceholderBuilder( - Uint8List? blurhash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) { return (context) => blurhash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index c1d34c4baa..8be71e9b2e 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget { double size; bool hasBorder; - UserCircleAvatar({ - super.key, - this.radius = 22, - this.size = 44, - this.hasBorder = false, - required this.user, - }); + UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -43,12 +37,7 @@ class UserCircleAvatar extends ConsumerWidget { child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - border: hasBorder - ? Border.all( - color: Colors.grey[500]!, - width: 1, - ) - : null, + border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null, ), child: CircleAvatar( backgroundColor: userAvatarColor, diff --git a/mobile/lib/widgets/forms/change_password_form.dart b/mobile/lib/widgets/forms/change_password_form.dart index d5fdf570dd..179b05a712 100644 --- a/mobile/lib/widgets/forms/change_password_form.dart +++ b/mobile/lib/widgets/forms/change_password_form.dart @@ -33,25 +33,13 @@ class ChangePasswordForm extends HookConsumerWidget { children: [ Text( 'change_password'.tr(), - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: context.primaryColor), ), Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: Text( - 'change_password_form_description'.tr( - namedArgs: { - 'name': authState.name, - }, - ), - style: TextStyle( - fontSize: 14, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w600, - ), + 'change_password_form_description'.tr(namedArgs: {'name': authState.name}), + style: TextStyle(fontSize: 14, color: context.colorScheme.onSurface, fontWeight: FontWeight.w600), ), ), Form( @@ -70,8 +58,9 @@ class ChangePasswordForm extends HookConsumerWidget { passwordController: passwordController, onPressed: () async { if (formKey.currentState!.validate()) { - var isSuccess = - await ref.read(authProvider.notifier).changePassword(passwordController.value.text); + var isSuccess = await ref + .read(authProvider.notifier) + .changePassword(passwordController.value.text); if (isSuccess) { await ref.read(authProvider.notifier).logout(); @@ -139,11 +128,7 @@ class ConfirmPasswordInput extends StatelessWidget { final TextEditingController originalController; final TextEditingController confirmController; - const ConfirmPasswordInput({ - super.key, - required this.originalController, - required this.confirmController, - }); + const ConfirmPasswordInput({super.key, required this.originalController, required this.confirmController}); String? _validateInput(String? email) { if (confirmController.value != originalController.value) { @@ -171,11 +156,7 @@ class ConfirmPasswordInput extends StatelessWidget { class ChangePasswordButton extends ConsumerWidget { final TextEditingController passwordController; final VoidCallback onPressed; - const ChangePasswordButton({ - super.key, - required this.passwordController, - required this.onPressed, - }); + const ChangePasswordButton({super.key, required this.passwordController, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -185,10 +166,7 @@ class ChangePasswordButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), ), onPressed: onPressed, - child: Text( - 'change_password'.tr(), - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + child: Text('change_password'.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/email_input.dart b/mobile/lib/widgets/forms/login/email_input.dart index 52f2a598f9..4d90d918ac 100644 --- a/mobile/lib/widgets/forms/login/email_input.dart +++ b/mobile/lib/widgets/forms/login/email_input.dart @@ -6,12 +6,7 @@ class EmailInput extends StatelessWidget { final FocusNode? focusNode; final Function()? onSubmit; - const EmailInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const EmailInput({super.key, required this.controller, this.focusNode, this.onSubmit}); String? _validateInput(String? email) { if (email == null || email == '') return null; @@ -32,10 +27,7 @@ class EmailInput extends StatelessWidget { labelText: 'email'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), ), validator: _validateInput, autovalidateMode: AutovalidateMode.always, diff --git a/mobile/lib/widgets/forms/login/loading_icon.dart b/mobile/lib/widgets/forms/login/loading_icon.dart index 9d3f5eab64..052ce43ac7 100644 --- a/mobile/lib/widgets/forms/login/loading_icon.dart +++ b/mobile/lib/widgets/forms/login/loading_icon.dart @@ -7,15 +7,7 @@ class LoadingIcon extends StatelessWidget { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.only(top: 18.0), - child: SizedBox( - width: 24, - height: 24, - child: FittedBox( - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - ), + child: SizedBox(width: 24, height: 24, child: FittedBox(child: CircularProgressIndicator(strokeWidth: 2))), ); } } diff --git a/mobile/lib/widgets/forms/login/login_button.dart b/mobile/lib/widgets/forms/login/login_button.dart index 479c53a9b7..0f9fb21d8f 100644 --- a/mobile/lib/widgets/forms/login/login_button.dart +++ b/mobile/lib/widgets/forms/login/login_button.dart @@ -5,23 +5,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; class LoginButton extends ConsumerWidget { final Function() onPressed; - const LoginButton({ - super.key, - required this.onPressed, - }); + const LoginButton({super.key, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton.icon( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), onPressed: onPressed, icon: const Icon(Icons.login_rounded), - label: const Text( - "login", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text("login", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ); } } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 7ac070b912..b4944ee1e9 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -54,9 +54,7 @@ class LoginForm extends HookConsumerWidget { final isOauthEnable = useState(false); final isPasswordLoginEnable = useState(false); final oAuthButtonLabel = useState('OAuth'); - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 60), - )..repeat(); + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 60))..repeat(); final serverInfo = ref.watch(serverInfoProvider); final warningMessage = useState(null); final loginFormKey = GlobalKey(); @@ -90,11 +88,7 @@ class LoginForm extends HookConsumerWidget { // Guard empty URL if (serverUrl.isEmpty) { - ImmichToast.show( - context: context, - msg: "login_form_server_empty".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "login_form_server_empty".tr(), toastType: ToastType.error); } try { @@ -148,16 +142,13 @@ class LoginForm extends HookConsumerWidget { isLoadingServer.value = false; } - useEffect( - () { - final serverUrl = getServerUrl(); - if (serverUrl != null) { - serverEndpointController.text = serverUrl; - } - return null; - }, - [], - ); + useEffect(() { + final serverUrl = getServerUrl(); + if (serverUrl != null) { + serverEndpointController.text = serverUrl; + } + return null; + }, []); populateTestLoginInfo() { emailController.text = 'demo@immich.app'; @@ -180,10 +171,7 @@ class LoginForm extends HookConsumerWidget { invalidateAllApiRepositoryProviders(ref); try { - final result = await ref.read(authProvider.notifier).login( - emailController.text, - passwordController.text, - ); + final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); if (result.shouldChangePassword && !result.isAdmin) { context.pushRoute(const ChangePasswordRoute()); @@ -212,12 +200,7 @@ class LoginForm extends HookConsumerWidget { String generateRandomString(int length) { const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; final random = Random.secure(); - return String.fromCharCodes( - Iterable.generate( - length, - (_) => chars.codeUnitAt(random.nextInt(chars.length)), - ), - ); + return String.fromCharCodes(Iterable.generate(length, (_) => chars.codeUnitAt(random.nextInt(chars.length)))); } List randomBytes(int length) { @@ -273,23 +256,17 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = await oAuthService.oAuthLogin( - oAuthServerUrl, - state, - codeVerifier, - ); + final loginResponseDto = await oAuthService.oAuthLogin(oAuthServerUrl, state, codeVerifier); if (loginResponseDto == null) { return; } - log.info( - "Finished OAuth login with response: ${loginResponseDto.userEmail}", - ); + log.info("Finished OAuth login with response: ${loginResponseDto.userEmail}"); - final isSuccess = await ref.watch(authProvider.notifier).saveAuthInfo( - accessToken: loginResponseDto.accessToken, - ); + final isSuccess = await ref + .watch(authProvider.notifier) + .saveAuthInfo(accessToken: loginResponseDto.accessToken); if (isSuccess) { isLoading.value = false; @@ -374,10 +351,7 @@ class LoginForm extends HookConsumerWidget { ), onPressed: isLoadingServer.value ? null : getServerAuthSettings, icon: const Icon(Icons.arrow_forward_rounded), - label: const Text( - 'next', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text('next', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ], @@ -401,17 +375,10 @@ class LoginForm extends HookConsumerWidget { padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), - border: Border.all( - color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!, - ), - ), - child: Text( - warningMessage.value!, - textAlign: TextAlign.center, + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border.all(color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!), ), + child: Text(warningMessage.value!, textAlign: TextAlign.center), ), ); } @@ -435,11 +402,7 @@ class LoginForm extends HookConsumerWidget { onSubmit: passwordFocusNode.requestFocus, ), const SizedBox(height: 8), - PasswordInput( - controller: passwordController, - focusNode: passwordFocusNode, - onSubmit: login, - ), + PasswordInput(controller: passwordController, focusNode: passwordFocusNode, onSubmit: login), ], // Note: This used to have an AnimatedSwitcher, but was removed @@ -455,12 +418,8 @@ class LoginForm extends HookConsumerWidget { if (isOauthEnable.value) ...[ if (isPasswordLoginEnable.value) Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Divider( - color: context.isDarkTheme ? Colors.white : Colors.black, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Divider(color: context.isDarkTheme ? Colors.white : Colors.black), ), OAuthLoginButton( serverEndpointController: serverEndpointController, @@ -471,10 +430,7 @@ class LoginForm extends HookConsumerWidget { ], ], ), - if (!isOauthEnable.value && !isPasswordLoginEnable.value) - Center( - child: const Text('login_disabled').tr(), - ), + if (!isOauthEnable.value && !isPasswordLoginEnable.value) Center(child: const Text('login_disabled').tr()), const SizedBox(height: 12), TextButton.icon( icon: const Icon(Icons.arrow_back), @@ -498,9 +454,7 @@ class LoginForm extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( - height: constraints.maxHeight / 5, - ), + SizedBox(height: constraints.maxHeight / 5), Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, @@ -510,24 +464,16 @@ class LoginForm extends HookConsumerWidget { onLongPress: () => populateTestLoginInfo1(), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0, bottom: 16), - child: ImmichTitleText(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0, bottom: 16), child: ImmichTitleText()), ], ), // Note: This used to have an AnimatedSwitcher, but was removed // because of https://github.com/flutter/flutter/issues/120874 - Form( - key: loginFormKey, - child: serverSelectionOrLogin, - ), + Form(key: loginFormKey, child: serverSelectionOrLogin), ], ), ), diff --git a/mobile/lib/widgets/forms/login/o_auth_login_button.dart b/mobile/lib/widgets/forms/login/o_auth_login_button.dart index 465d88a4d2..2d9b603b3c 100644 --- a/mobile/lib/widgets/forms/login/o_auth_login_button.dart +++ b/mobile/lib/widgets/forms/login/o_auth_login_button.dart @@ -25,10 +25,7 @@ class OAuthLoginButton extends ConsumerWidget { ), onPressed: onPressed, icon: const Icon(Icons.pin_rounded), - label: Text( - buttonLabel, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + label: Text(buttonLabel, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/password_input.dart b/mobile/lib/widgets/forms/login/password_input.dart index 074899bd57..5cdfcc9567 100644 --- a/mobile/lib/widgets/forms/login/password_input.dart +++ b/mobile/lib/widgets/forms/login/password_input.dart @@ -8,12 +8,7 @@ class PasswordInput extends HookConsumerWidget { final FocusNode? focusNode; final Function()? onSubmit; - const PasswordInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const PasswordInput({super.key, required this.controller, this.focusNode, this.onSubmit}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,15 +21,10 @@ class PasswordInput extends HookConsumerWidget { labelText: 'password'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), suffixIcon: IconButton( onPressed: () => isPasswordVisible.value = !isPasswordVisible.value, - icon: Icon( - isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp, - ), + icon: Icon(isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp), ), ), autofillHints: const [AutofillHints.password], diff --git a/mobile/lib/widgets/forms/login/server_endpoint_input.dart b/mobile/lib/widgets/forms/login/server_endpoint_input.dart index cddf9e9985..f9bc1690af 100644 --- a/mobile/lib/widgets/forms/login/server_endpoint_input.dart +++ b/mobile/lib/widgets/forms/login/server_endpoint_input.dart @@ -7,12 +7,7 @@ class ServerEndpointInput extends StatelessWidget { final FocusNode focusNode; final Function()? onSubmit; - const ServerEndpointInput({ - super.key, - required this.controller, - required this.focusNode, - this.onSubmit, - }); + const ServerEndpointInput({super.key, required this.controller, required this.focusNode, this.onSubmit}); String? _validateInput(String? url) { if (url == null || url.isEmpty) return null; diff --git a/mobile/lib/widgets/forms/pin_input.dart b/mobile/lib/widgets/forms/pin_input.dart index 6602946d7d..88e27f005e 100644 --- a/mobile/lib/widgets/forms/pin_input.dart +++ b/mobile/lib/widgets/forms/pin_input.dart @@ -43,11 +43,7 @@ class PinInput extends StatelessWidget { final defaultPinTheme = PinTheme( width: getPinSize().width, height: getPinSize().height, - textStyle: TextStyle( - fontSize: 24, - color: context.colorScheme.onSurface, - fontFamily: 'Overpass Mono', - ), + textStyle: TextStyle(fontSize: 24, color: context.colorScheme.onSurface, fontFamily: 'Overpass Mono'), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), border: Border.all(color: context.colorScheme.surfaceBright), @@ -70,34 +66,19 @@ class PinInput extends StatelessWidget { forceErrorState: hasError ?? false, autofocus: autoFocus ?? false, obscureText: obscureText ?? false, - obscuringWidget: Icon( - Icons.vpn_key_rounded, - color: context.primaryColor, - size: 20, - ), - separatorBuilder: (index) => const SizedBox( - height: 64, - width: 3, - ), + obscuringWidget: Icon(Icons.vpn_key_rounded, color: context.primaryColor, size: 20), + separatorBuilder: (index) => const SizedBox(height: 64, width: 3), cursor: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Container( - margin: const EdgeInsets.only(bottom: 9), - width: 18, - height: 2, - color: context.primaryColor, - ), + Container(margin: const EdgeInsets.only(bottom: 9), width: 18, height: 2, color: context.primaryColor), ], ), defaultPinTheme: defaultPinTheme, focusedPinTheme: defaultPinTheme.copyWith( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.primaryColor.withValues(alpha: 0.5), - width: 2, - ), + border: Border.all(color: context.primaryColor.withValues(alpha: 0.5), width: 2), color: context.colorScheme.surfaceContainerHigh, ), ), @@ -105,10 +86,7 @@ class PinInput extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.error.withAlpha(15), borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.colorScheme.error.withAlpha(100), - width: 2, - ), + border: Border.all(color: context.colorScheme.error.withAlpha(100), width: 2), ), ), pinputAutovalidateMode: PinputAutovalidateMode.onSubmit, diff --git a/mobile/lib/widgets/forms/pin_registration_form.dart b/mobile/lib/widgets/forms/pin_registration_form.dart index c3cfd3a864..d126169aad 100644 --- a/mobile/lib/widgets/forms/pin_registration_form.dart +++ b/mobile/lib/widgets/forms/pin_registration_form.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/widgets/forms/pin_input.dart'; class PinRegistrationForm extends HookConsumerWidget { final Function() onDone; - const PinRegistrationForm({ - super.key, - required this.onDone, - }); + const PinRegistrationForm({super.key, required this.onDone}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -40,35 +37,25 @@ class PinRegistrationForm extends HookConsumerWidget { } try { - await ref.read(authProvider.notifier).setupPinCode( - newPinCodeController.text, - ); + await ref.read(authProvider.notifier).setupPinCode(newPinCodeController.text); onDone(); } catch (error) { hasError.value = true; - context.showSnackBar( - SnackBar(content: Text(error.toString())), - ); + context.showSnackBar(SnackBar(content: Text(error.toString()))); } } return Form( child: Column( children: [ - Icon( - Icons.pin_outlined, - size: 64, - color: context.primaryColor, - ), + Icon(Icons.pin_outlined, size: 64, color: context.primaryColor), const SizedBox(height: 32), SizedBox( width: context.width * 0.7, child: Text( 'setup_pin_code'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 24, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 24), textAlign: TextAlign.center, ), ), @@ -76,9 +63,7 @@ class PinRegistrationForm extends HookConsumerWidget { width: context.width * 0.8, child: Text( 'new_pin_code_subtitle'.tr(), - style: context.textTheme.bodyLarge!.copyWith( - fontSize: 16, - ), + style: context.textTheme.bodyLarge!.copyWith(fontSize: 16), textAlign: TextAlign.center, ), ), @@ -113,10 +98,7 @@ class PinRegistrationForm extends HookConsumerWidget { child: Row( children: [ Expanded( - child: ElevatedButton( - onPressed: createNewPinCode, - child: Text('create'.tr()), - ), + child: ElevatedButton(onPressed: createNewPinCode, child: Text('create'.tr())), ), ], ), diff --git a/mobile/lib/widgets/forms/pin_verification_form.dart b/mobile/lib/widgets/forms/pin_verification_form.dart index 8a3f0b55df..2b7e3e8251 100644 --- a/mobile/lib/widgets/forms/pin_verification_form.dart +++ b/mobile/lib/widgets/forms/pin_verification_form.dart @@ -49,11 +49,7 @@ class PinVerificationForm extends HookConsumerWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: isVerified.value - ? Icon( - successIcon ?? Icons.lock_open_rounded, - size: 64, - color: Colors.green[300], - ) + ? Icon(successIcon ?? Icons.lock_open_rounded, size: 64, color: Colors.green[300]) : Icon( icon ?? Icons.lock_outline_rounded, size: 64, @@ -65,9 +61,7 @@ class PinVerificationForm extends HookConsumerWidget { width: context.width * 0.7, child: Text( description ?? 'enter_your_pin_code_subtitle'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 18, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 18), textAlign: TextAlign.center, ), ), diff --git a/mobile/lib/widgets/map/map_app_bar.dart b/mobile/lib/widgets/map/map_app_bar.dart index 2715386737..73706c7661 100644 --- a/mobile/lib/widgets/map/map_app_bar.dart +++ b/mobile/lib/widgets/map/map_app_bar.dart @@ -52,16 +52,12 @@ class _NonSelectionRow extends StatelessWidget { children: [ ElevatedButton( onPressed: () => context.maybePop(), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ElevatedButton( onPressed: onSettingsPressed, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.more_vert_rounded), ), ], @@ -78,10 +74,7 @@ class _SelectionRow extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isProcessing = useProcessingOverlay(); - Future handleProcessing( - FutureOr Function() action, [ - bool reloadMarkers = false, - ]) async { + Future handleProcessing(FutureOr Function() action, [bool reloadMarkers = false]) async { isProcessing.value = true; await action(); // Reset state @@ -101,9 +94,7 @@ class _SelectionRow extends HookConsumerWidget { icon: const Icon(Icons.close_rounded), label: Text( '${selectedAssets.value.length}', - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onPrimary), ), ), ), @@ -112,43 +103,20 @@ class _SelectionRow extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( - onPressed: () => handleProcessing( - () => handleShareAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => handleProcessing(() => handleShareAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.ios_share_rounded), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleFavoriteAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleFavoriteAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.favorite), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleArchiveAssets( - ref, - context, - selectedAssets.value.toList(), - ), - true, - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleArchiveAssets(ref, context, selectedAssets.value.toList()), true), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.archive), ), ], diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index ce2a486fc5..893c36d43f 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -119,7 +119,8 @@ class MapAssetGrid extends HookConsumerWidget { final rowOffset = renderElement.offset; // Column offset = (total trailingEdge - trailingEdge crossed) / offset for each asset final totalOffset = item.itemTrailingEdge - item.itemLeadingEdge; - final edgeOffset = (totalOffset - partialOffset) / + final edgeOffset = + (totalOffset - partialOffset) / // Round the total count to the next multiple of [assetsPerRow] ((renderElement.totalCount / assetsPerRow) * assetsPerRow).floor(); @@ -146,34 +147,32 @@ class MapAssetGrid extends HookConsumerWidget { // Place it just below the drag handle heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty - ? ref.watch(assetsTimelineProvider(assetsInBounds.value)).when( - data: (renderList) { - // Cache render list here to use it back during visibleItemsListener - cachedRenderList.value = renderList; - return ValueListenableBuilder( - valueListenable: selectedAssets, - builder: (_, value, __) => ImmichAssetGrid( - shrinkWrap: true, - renderList: renderList, - showDragScroll: false, - assetsPerRow: assetsPerRow, - showMultiSelectIndicator: false, - selectionActive: value.isNotEmpty, - listener: onAssetsSelected, - visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), - ), - ); - }, - error: (error, stackTrace) { - log.warning( - "Cannot get assets in the current map bounds", - error, - stackTrace, - ); - return const SizedBox.shrink(); - }, - loading: () => const SizedBox.shrink(), - ) + ? ref + .watch(assetsTimelineProvider(assetsInBounds.value)) + .when( + data: (renderList) { + // Cache render list here to use it back during visibleItemsListener + cachedRenderList.value = renderList; + return ValueListenableBuilder( + valueListenable: selectedAssets, + builder: (_, value, __) => ImmichAssetGrid( + shrinkWrap: true, + renderList: renderList, + showDragScroll: false, + assetsPerRow: assetsPerRow, + showMultiSelectIndicator: false, + selectionActive: value.isNotEmpty, + listener: onAssetsSelected, + visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), + ), + ); + }, + error: (error, stackTrace) { + log.warning("Cannot get assets in the current map bounds", error, stackTrace); + return const SizedBox.shrink(); + }, + loading: () => const SizedBox.shrink(), + ) : const _MapNoAssetsInSheet(), ), ), @@ -194,11 +193,7 @@ class _MapNoAssetsInSheet extends StatelessWidget { @override Widget build(BuildContext context) { - const image = Image( - height: 150, - width: 150, - image: AssetImage('assets/lighthouse.png'), - ); + const image = Image(height: 150, width: 150, image: AssetImage('assets/lighthouse.png')); return Center( child: ListView( @@ -206,21 +201,12 @@ class _MapNoAssetsInSheet extends StatelessWidget { children: [ context.isDarkTheme ? const InvertionFilter( - child: SaturationFilter( - saturation: -1, - child: BrightnessFilter( - brightness: -5, - child: image, - ), - ), + child: SaturationFilter(saturation: -1, child: BrightnessFilter(brightness: -5, child: image)), ) : image, const SizedBox(height: 20), Center( - child: Text( - "map_zoom_to_see_photos".tr(), - style: context.textTheme.displayLarge?.copyWith(fontSize: 18), - ), + child: Text("map_zoom_to_see_photos".tr(), style: context.textTheme.displayLarge?.copyWith(fontSize: 18)), ), ], ), @@ -254,10 +240,7 @@ class _MapSheetDragRegion extends StatelessWidget { margin: EdgeInsets.zero, shape: context.isMobile ? const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), + borderRadius: BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)), ) : const BeveledRectangleBorder(), elevation: 0.0, @@ -291,10 +274,7 @@ class _MapSheetDragRegion extends StatelessWidget { right: 18, top: 24, child: IconButton( - icon: Icon( - Icons.map_outlined, - color: context.textTheme.displayLarge?.color, - ), + icon: Icon(Icons.map_outlined, color: context.textTheme.displayLarge?.color), iconSize: 24, tooltip: 'Zoom to bounds', onPressed: () => onZoomToAsset?.call(value!), diff --git a/mobile/lib/widgets/map/map_bottom_sheet.dart b/mobile/lib/widgets/map/map_bottom_sheet.dart index d8c1cc638e..baf85e8075 100644 --- a/mobile/lib/widgets/map/map_bottom_sheet.dart +++ b/mobile/lib/widgets/map/map_bottom_sheet.dart @@ -34,11 +34,7 @@ class MapBottomSheet extends HookConsumerWidget { void handleMapEvents(MapEvent event) async { if (event is MapCloseBottomSheet) { - sheetController.animateTo( - 0.1, - duration: const Duration(milliseconds: 200), - curve: Curves.linearToEaseOut, - ); + sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut); } } @@ -85,9 +81,7 @@ class MapBottomSheet extends HookConsumerWidget { duration: const Duration(milliseconds: 150), child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), diff --git a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart index 5c755d80be..51567eff15 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart @@ -8,21 +8,13 @@ class MapSettingsListTile extends StatelessWidget { final bool selected; final Function(bool) onChanged; - const MapSettingsListTile({ - super.key, - required this.title, - required this.selected, - required this.onChanged, - }); + const MapSettingsListTile({super.key, required this.title, required this.selected, required this.onChanged}); @override Widget build(BuildContext context) { return SwitchListTile.adaptive( activeColor: context.primaryColor, - title: Text( - title, - style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text(title, style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), value: selected, onChanged: onChanged, ); diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart index a627ff8f29..b601887e1e 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart @@ -5,11 +5,7 @@ class MapTimeDropDown extends StatelessWidget { final int relativeTime; final Function(int) onTimeChange; - const MapTimeDropDown({ - super.key, - required this.relativeTime, - required this.onTimeChange, - }); + const MapTimeDropDown({super.key, required this.relativeTime, required this.onTimeChange}); @override Widget build(BuildContext context) { @@ -20,10 +16,7 @@ class MapTimeDropDown extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - "date_range".tr(), - style: const TextStyle(fontWeight: FontWeight.bold), - ), + child: Text("date_range".tr(), style: const TextStyle(fontWeight: FontWeight.bold)), ), LayoutBuilder( builder: (_, constraints) => DropdownMenu( @@ -33,53 +26,19 @@ class MapTimeDropDown extends StatelessWidget { initialSelection: relativeTime, onSelected: (value) => onTimeChange(value!), dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "all".tr(), - ), - DropdownMenuEntry( - value: 1, - label: "map_settings_date_range_option_day".tr(), - ), - DropdownMenuEntry( - value: 7, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "7"}, - ), - ), - DropdownMenuEntry( - value: 30, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "30"}, - ), - ), + DropdownMenuEntry(value: 0, label: "all".tr()), + DropdownMenuEntry(value: 1, label: "map_settings_date_range_option_day".tr()), + DropdownMenuEntry(value: 7, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "7"})), + DropdownMenuEntry(value: 30, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "30"})), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 1, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 1, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_year".tr(), ), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 3, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 3, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_years".tr(namedArgs: {'years': "3"}), ), diff --git a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart index 747ae06a54..63f35ebe4c 100644 --- a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart +++ b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart @@ -8,11 +8,7 @@ class MapThemePicker extends StatelessWidget { final ThemeMode themeMode; final Function(ThemeMode) onThemeChange; - const MapThemePicker({ - super.key, - required this.themeMode, - required this.onThemeChange, - }); + const MapThemePicker({super.key, required this.themeMode, required this.onThemeChange}); @override Widget build(BuildContext context) { @@ -76,10 +72,7 @@ class _BorderedMapThumbnail extends StatelessWidget { Container( decoration: BoxDecoration( border: Border.fromBorderSide( - BorderSide( - width: 4, - color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent, - ), + BorderSide(width: 4, color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent), ), borderRadius: const BorderRadius.all(Radius.circular(20)), ), @@ -95,9 +88,7 @@ class _BorderedMapThumbnail extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: Text( name, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: shouldHighlight ? FontWeight.bold : null, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: shouldHighlight ? FontWeight.bold : null), ), ), ], diff --git a/mobile/lib/widgets/map/map_theme_override.dart b/mobile/lib/widgets/map/map_theme_override.dart index 3f9ae0f43f..57f970b0d1 100644 --- a/mobile/lib/widgets/map/map_theme_override.dart +++ b/mobile/lib/widgets/map/map_theme_override.dart @@ -85,11 +85,7 @@ class _MapThemeOverrideState extends ConsumerState with Widget ? getThemeData(colorScheme: appTheme.dark, locale: locale) : getThemeData(colorScheme: appTheme.light, locale: locale), child: widget.mapBuilder.call( - ref.watch( - mapStateNotifierProvider.select( - (v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched, - ), - ), + ref.watch(mapStateNotifierProvider.select((v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched)), ), ); } diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 0dc1ad3a4f..55f5ff77c6 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -110,11 +110,7 @@ class MapThumbnail extends HookConsumerWidget { ValueListenableBuilder( valueListenable: position, builder: (_, value, __) => value != null && assetMarkerRemoteId != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) + ? PositionedAssetMarkerIcon(size: height / 2, point: value, assetRemoteId: assetMarkerRemoteId!) : const SizedBox.shrink(), ), ], diff --git a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart index 6207a6ab56..0944f7ce3e 100644 --- a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart +++ b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart @@ -35,10 +35,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { onTap: () => onTap?.call(), child: SizedBox.square( dimension: size, - child: _AssetMarkerIcon( - id: assetRemoteId, - key: Key(assetRemoteId), - ), + child: _AssetMarkerIcon(id: assetRemoteId, key: Key(assetRemoteId)), ), ), ); @@ -46,10 +43,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { } class _AssetMarkerIcon extends StatelessWidget { - const _AssetMarkerIcon({ - required this.id, - super.key, - }); + const _AssetMarkerIcon({required this.id, super.key}); final String id; @@ -71,10 +65,7 @@ class _AssetMarkerIcon extends StatelessWidget { primaryRadius: constraints.maxHeight * 0.06, secondaryRadius: constraints.maxHeight * 0.038, ), - child: SizedBox( - height: constraints.maxHeight * 0.14, - width: constraints.maxWidth * 0.14, - ), + child: SizedBox(height: constraints.maxHeight * 0.14, width: constraints.maxWidth * 0.14), ), ), Positioned( @@ -129,26 +120,11 @@ class _PinPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 2; - canvas.drawCircle( - Offset(size.width / 2, size.height), - primaryRadius, - primaryBrush, - ); - canvas.drawCircle( - Offset(size.width / 2, size.height), - secondaryRadius, - secondaryBrush, - ); + canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush); + canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush); canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush); // The line is to make the above triangluar path more prominent since it has a slight curve - canvas.drawLine( - Offset(size.width / 2, 0), - Offset( - size.width / 2, - size.height, - ), - lineBrush, - ); + canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush); } Path getTrianglePath(double x, double y) { @@ -157,18 +133,8 @@ class _PinPainter extends CustomPainter { final secondEndPoint = Offset(x, 0); return Path() - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - firstEndPoint.dx, - firstEndPoint.dy, - ) - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - secondEndPoint.dx, - secondEndPoint.dy, - ) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy) ..lineTo(0, 0); } diff --git a/mobile/lib/widgets/memories/memory_bottom_info.dart b/mobile/lib/widgets/memories/memory_bottom_info.dart index 1797b5c1c3..4b43821782 100644 --- a/mobile/lib/widgets/memories/memory_bottom_info.dart +++ b/mobile/lib/widgets/memories/memory_bottom_info.dart @@ -16,45 +16,35 @@ class MemoryBottomInfo extends StatelessWidget { final df = DateFormat.yMMMMd(); return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - memory.title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + memory.title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format( - memory.assets[0].fileCreatedAt, + Text( + df.format(memory.assets[0].fileCreatedAt), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + MaterialButton( + minWidth: 0, + onPressed: () { + context.maybePop(); + scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ], + ), ); } } diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index 1faa114936..189cc67428 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -14,13 +14,7 @@ class MemoryCard extends StatelessWidget { final bool showTitle; final Function()? onVideoEnded; - const MemoryCard({ - required this.asset, - required this.title, - required this.showTitle, - this.onVideoEnded, - super.key, - }); + const MemoryCard({required this.asset, required this.title, required this.showTitle, this.onVideoEnded, super.key}); @override Widget build(BuildContext context) { @@ -28,17 +22,12 @@ class MemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio @@ -56,12 +45,7 @@ class MemoryCard extends StatelessWidget { if (asset.isImage) { return Hero( tag: 'memory-${asset.id}', - child: ImmichImage( - asset, - fit: fit, - height: double.infinity, - width: double.infinity, - ), + child: ImmichImage(asset, fit: fit, height: double.infinity, width: double.infinity), ); } else { return Hero( @@ -74,12 +58,7 @@ class MemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: ImmichImage( - asset, - width: context.width, - height: context.height, - fit: BoxFit.contain, - ), + image: ImmichImage(asset, width: context.width, height: context.height, fit: BoxFit.contain), ), ), ); @@ -92,10 +71,7 @@ class MemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -116,16 +92,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -136,17 +105,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: ImmichImage.imageProvider( - asset: asset, - height: context.height, - width: context.width, - ), + image: ImmichImage.imageProvider(asset: asset, height: context.height, width: context.width), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/widgets/memories/memory_epilogue.dart b/mobile/lib/widgets/memories/memory_epilogue.dart index 10349aa431..b866ed049a 100644 --- a/mobile/lib/widgets/memories/memory_epilogue.dart +++ b/mobile/lib/widgets/memories/memory_epilogue.dart @@ -12,24 +12,15 @@ class MemoryEpilogue extends StatefulWidget { } class _MemoryEpilogueState extends State with TickerProviderStateMixin { - late final _animationController = AnimationController( - vsync: this, - duration: const Duration( - seconds: 2, - ), - )..repeat( - reverse: true, - ); + late final _animationController = AnimationController(vsync: this, duration: const Duration(seconds: 2)) + ..repeat(reverse: true); late final Animation _animation; @override void initState() { super.initState(); - _animation = CurvedAnimation( - parent: _animationController, - curve: Curves.easeIn, - ); + _animation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn); } @override @@ -55,16 +46,12 @@ class _MemoryEpilogueState extends State with TickerProviderStat const SizedBox(height: 16.0), Text( "memories_all_caught_up", - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), Text( "memories_check_back_tomorrow", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), TextButton( @@ -92,23 +79,14 @@ class _MemoryEpilogueState extends State with TickerProviderStat child: AnimatedBuilder( animation: _animation, builder: (context, child) { - return Transform.translate( - offset: Offset(0, 8 * _animationController.value), - child: child, - ); + return Transform.translate(offset: Offset(0, 8 * _animationController.value), child: child); }, - child: const Icon( - size: 32, - Icons.expand_less_sharp, - color: Colors.white, - ), + child: const Icon(size: 32, Icons.expand_less_sharp, color: Colors.white), ), ), Text( "memories_swipe_to_close", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), ], ), diff --git a/mobile/lib/widgets/memories/memory_lane.dart b/mobile/lib/widgets/memories/memory_lane.dart index 3f97bd1ea4..727950fd86 100644 --- a/mobile/lib/widgets/memories/memory_lane.dart +++ b/mobile/lib/widgets/memories/memory_lane.dart @@ -22,17 +22,13 @@ class MemoryLane extends HookConsumerWidget { .whenData( (memories) => memories != null ? ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (memoryIndex) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); if (memories[memoryIndex].assets.isNotEmpty) { @@ -42,20 +38,10 @@ class MemoryLane extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); } } - context.pushRoute( - MemoryRoute( - memories: memories, - memoryIndex: memoryIndex, - ), - ); + context.pushRoute(MemoryRoute(memories: memories, memoryIndex: memoryIndex)); }, children: memories - .mapIndexed( - (index, memory) => MemoryCard( - index: index, - memory: memory, - ), - ) + .mapIndexed((index, memory) => MemoryCard(index: index, memory: memory)) .toList(), ), ) @@ -68,11 +54,7 @@ class MemoryLane extends HookConsumerWidget { } class MemoryCard extends ConsumerWidget { - const MemoryCard({ - super.key, - required this.index, - required this.memory, - }); + const MemoryCard({super.key, required this.index, required this.memory}); final int index; final Memory memory; @@ -83,10 +65,7 @@ class MemoryCard extends ConsumerWidget { child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: Hero( tag: 'memory-${memory.assets[0].id}', child: ImmichImage( @@ -94,10 +73,7 @@ class MemoryCard extends ConsumerWidget { fit: BoxFit.cover, width: 205, height: 200, - placeholder: const ThumbnailPlaceholder( - width: 105, - height: 200, - ), + placeholder: const ThumbnailPlaceholder(width: 105, height: 200), ), ), ), @@ -105,16 +81,10 @@ class MemoryCard extends ConsumerWidget { bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( memory.title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/widgets/memories/memory_progress_indicator.dart b/mobile/lib/widgets/memories/memory_progress_indicator.dart index 646846cd11..aab1dc1a97 100644 --- a/mobile/lib/widgets/memories/memory_progress_indicator.dart +++ b/mobile/lib/widgets/memories/memory_progress_indicator.dart @@ -8,11 +8,7 @@ class MemoryProgressIndicator extends StatelessWidget { /// The current value of the indicator final double value; - const MemoryProgressIndicator({ - super.key, - required this.ticks, - required this.value, - }); + const MemoryProgressIndicator({super.key, required this.ticks, required this.value}); @override Widget build(BuildContext context) { @@ -37,14 +33,7 @@ class MemoryProgressIndicator extends StatelessWidget { width: tickWidth, height: 4, decoration: BoxDecoration( - border: i == 0 - ? null - : const Border( - left: BorderSide( - color: Colors.black, - width: 1, - ), - ), + border: i == 0 ? null : const Border(left: BorderSide(color: Colors.black, width: 1)), ), ), ), diff --git a/mobile/lib/widgets/photo_view/photo_view.dart b/mobile/lib/widgets/photo_view/photo_view.dart index 0b769d559b..69be96ed53 100644 --- a/mobile/lib/widgets/photo_view/photo_view.dart +++ b/mobile/lib/widgets/photo_view/photo_view.dart @@ -15,9 +15,7 @@ export 'src/photo_view_scale_state.dart'; export 'src/utils/photo_view_hero_attributes.dart'; typedef PhotoViewControllerCallback = PhotoViewControllerBase Function(); -typedef PhotoViewControllerCallbackBuilder = void Function( - PhotoViewControllerCallback photoViewMethod, -); +typedef PhotoViewControllerCallbackBuilder = void Function(PhotoViewControllerCallback photoViewMethod); /// A [StatefulWidget] that contains all the photo view rendering elements. /// @@ -269,8 +267,8 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.errorBuilder, this.enablePanAlways, - }) : child = null, - childSize = null; + }) : child = null, + childSize = null; /// Creates a widget that displays a zoomable child. /// @@ -310,12 +308,12 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.disableGestures, this.enablePanAlways, - }) : semanticLabel = null, - errorBuilder = null, - imageProvider = null, - gaplessPlayback = false, - loadingBuilder = null, - index = 0; + }) : semanticLabel = null, + errorBuilder = null, + imageProvider = null, + gaplessPlayback = false, + loadingBuilder = null, + index = 0; /// Given a [imageProvider] it resolves into an zoomable image widget using. It /// is required @@ -543,10 +541,7 @@ class _PhotoViewState extends State with AutomaticKeepAliveClientMixi Widget build(BuildContext context) { super.build(context); return LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { + builder: (BuildContext context, BoxConstraints constraints) { final computedOuterSize = widget.customSize ?? constraints.biggest; final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.black); @@ -623,72 +618,49 @@ class _PhotoViewState extends State with AutomaticKeepAliveClientMixi /// The default [ScaleStateCycle] PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => switch (actual) { - PhotoViewScaleState.initial => PhotoViewScaleState.covering, - PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, - PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, - PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, - }; + PhotoViewScaleState.initial => PhotoViewScaleState.covering, + PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, + PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, + PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, +}; /// A type definition for a [Function] that receives the actual [PhotoViewScaleState] and returns the next one /// It is used internally to walk in the "doubletap gesture cycle". /// It is passed to [PhotoView.scaleStateCycle] -typedef ScaleStateCycle = PhotoViewScaleState Function( - PhotoViewScaleState actual, -); +typedef ScaleStateCycle = PhotoViewScaleState Function(PhotoViewScaleState actual); /// A type definition for a callback when the user taps up the photoview region -typedef PhotoViewImageTapUpCallback = Function( - BuildContext context, - TapUpDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapUpCallback = + Function(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageTapDownCallback = Function( - BuildContext context, - TapDownDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapDownCallback = + Function(BuildContext context, TapDownDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user drags up -typedef PhotoViewImageDragStartCallback = Function( - BuildContext context, - DragStartDetails details, - PhotoViewControllerBase controllerValue, - PhotoViewScaleStateController scaleStateController, -); +typedef PhotoViewImageDragStartCallback = + Function( + BuildContext context, + DragStartDetails details, + PhotoViewControllerBase controllerValue, + PhotoViewScaleStateController scaleStateController, + ); /// A type definition for a callback when the user drags -typedef PhotoViewImageDragUpdateCallback = Function( - BuildContext context, - DragUpdateDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragUpdateCallback = + Function(BuildContext context, DragUpdateDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageDragEndCallback = Function( - BuildContext context, - DragEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragEndCallback = + Function(BuildContext context, DragEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when a user finished scale -typedef PhotoViewImageScaleEndCallback = Function( - BuildContext context, - ScaleEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageScaleEndCallback = + Function(BuildContext context, ScaleEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user long press start -typedef PhotoViewImageLongPressStartCallback = Function( - BuildContext context, - LongPressStartDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageLongPressStartCallback = + Function(BuildContext context, LongPressStartDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress -typedef LoadingBuilder = Widget Function( - BuildContext context, - ImageChunkEvent? event, - int index, -); +typedef LoadingBuilder = Widget Function(BuildContext context, ImageChunkEvent? event, int index); diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 3f8188f7e2..3d56f3f657 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -20,16 +20,10 @@ import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart'; /// A type definition for a [Function] that receives a index after a page change in [PhotoViewGallery] -typedef PhotoViewGalleryPageChangedCallback = void Function( - int index, - PhotoViewControllerBase? controller, -); +typedef PhotoViewGalleryPageChangedCallback = void Function(int index, PhotoViewControllerBase? controller); /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build] -typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function( - BuildContext context, - int index, -); +typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function(BuildContext context, int index); /// A [StatefulWidget] that shows multiple [PhotoView] widgets in a [PageView] /// @@ -126,8 +120,8 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : itemCount = null, - builder = null; + }) : itemCount = null, + builder = null; /// Construct a gallery with dynamic items. /// @@ -151,9 +145,9 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : pageOptions = null, - assert(itemCount != null), - assert(builder != null); + }) : pageOptions = null, + assert(itemCount != null), + assert(builder != null); /// A list of options to describe the items in the gallery final List? pageOptions; @@ -340,15 +334,10 @@ class _PhotoViewGalleryState extends State { heroAttributes: pageOption.heroAttributes, ); - return ClipRect( - child: photoView, - ); + return ClipRect(child: photoView); } - PhotoViewGalleryPageOptions _buildPageOption( - BuildContext context, - int index, - ) { + PhotoViewGalleryPageOptions _buildPageOption(BuildContext context, int index) { if (widget._isBuilder) { return widget.builder!(context, index); } @@ -386,9 +375,9 @@ class PhotoViewGalleryPageOptions { this.disableScaleGestures, this.disableGestures, this.errorBuilder, - }) : child = null, - childSize = null, - assert(imageProvider != null); + }) : child = null, + childSize = null, + assert(imageProvider != null); const PhotoViewGalleryPageOptions.customChild({ this.key, @@ -415,8 +404,8 @@ class PhotoViewGalleryPageOptions { this.filterQuality, this.disableScaleGestures, this.disableGestures, - }) : errorBuilder = null, - imageProvider = null; + }) : errorBuilder = null, + imageProvider = null; final Key? key; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart index 6a860695b2..2c8b406385 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart @@ -72,12 +72,7 @@ abstract class PhotoViewControllerBase { Offset? rotationFocusPoint; /// Update multiple fields of the state with only one update streamed. - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }); + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}); } /// The state value stored and streamed by [PhotoViewController]. @@ -122,19 +117,16 @@ class PhotoViewControllerValue { /// For details of fields and methods, check [PhotoViewControllerBase]. /// class PhotoViewController implements PhotoViewControllerBase { - PhotoViewController({ - Offset initialPosition = Offset.zero, - double initialRotation = 0.0, - double? initialScale, - }) : _valueNotifier = IgnorableValueNotifier( - PhotoViewControllerValue( - position: initialPosition, - rotation: initialRotation, - scale: initialScale, - rotationFocusPoint: null, - ), + PhotoViewController({Offset initialPosition = Offset.zero, double initialRotation = 0.0, double? initialScale}) + : _valueNotifier = IgnorableValueNotifier( + PhotoViewControllerValue( + position: initialPosition, + rotation: initialRotation, + scale: initialScale, + rotationFocusPoint: null, ), - super() { + ), + super() { initial = value; prevValue = initial; @@ -299,12 +291,7 @@ class PhotoViewController implements PhotoViewControllerBase value.rotationFocusPoint; @override - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { prevValue = value; value = PhotoViewControllerValue( position: position ?? value.position, diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart index d28577ea49..6825bf8ee1 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart @@ -35,23 +35,15 @@ mixin PhotoViewControllerDelegate on State { controller.setScaleInvisibly(scale); return; } - final double prevScale = controller.scale ?? - getScaleForScaleState( - scaleStateController.prevScaleState, - scaleBoundaries, - ); + final double prevScale = + controller.scale ?? getScaleForScaleState(scaleStateController.prevScaleState, scaleBoundaries); - final double nextScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final double nextScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); _animateScale!(prevScale, nextScale); } - void addAnimateOnScaleStateUpdate( - void Function(double prevScale, double nextScale) animateScale, - ) { + void addAnimateOnScaleStateUpdate(void Function(double prevScale, double nextScale) animateScale) { _animateScale = animateScale; } @@ -62,8 +54,9 @@ mixin PhotoViewControllerDelegate on State { if (controller.scale == controller.prevValue.scale) { return; } - final PhotoViewScaleState newScaleState = - (scale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + final PhotoViewScaleState newScaleState = (scale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; scaleStateController.setInvisibly(newScaleState); } @@ -76,10 +69,7 @@ mixin PhotoViewControllerDelegate on State { final scaleExistsOnController = controller.scale != null; if (needsRecalc || !scaleExistsOnController) { - final newScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final newScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); markNeedsScaleRecalc = false; scale = newScale; return newScale; @@ -89,12 +79,7 @@ mixin PhotoViewControllerDelegate on State { set scale(double scale) => controller.setScaleInvisibly(scale); - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { controller.updateMultiple( position: position, scale: scale, @@ -106,8 +91,9 @@ mixin PhotoViewControllerDelegate on State { PhotoViewScaleState getScaleStateFromNewScale(double newScale) { PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; if (scale != scaleBoundaries.initialScale) { - newScaleState = - (newScale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + newScaleState = (newScale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; } return newScaleState; } @@ -115,8 +101,9 @@ mixin PhotoViewControllerDelegate on State { void updateScaleStateFromNewScale(double newScale) { PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; if (scale != scaleBoundaries.initialScale) { - newScaleState = - (newScale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + newScaleState = (newScale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; } scaleStateController.setInvisibly(newScaleState); } @@ -127,10 +114,7 @@ mixin PhotoViewControllerDelegate on State { scaleStateController.scaleState = scaleStateCycle(scaleState); return; } - final double originalScale = getScaleForScaleState( - scaleState, - scaleBoundaries, - ); + final double originalScale = getScaleForScaleState(scaleState, scaleBoundaries); double prevScale = originalScale; PhotoViewScaleState prevScaleState = scaleState; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart index e96aff7780..8d078db2c8 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart @@ -19,8 +19,9 @@ typedef ScaleStateListener = void Function(double prevScale, double nextScale); /// The updates should be done via [scaleState] setter and the updated listened via [outputScaleStateStream] /// class PhotoViewScaleStateController { - late final IgnorableValueNotifier _scaleStateNotifier = - IgnorableValueNotifier(PhotoViewScaleState.initial)..addListener(_scaleStateChangeListener); + late final IgnorableValueNotifier _scaleStateNotifier = IgnorableValueNotifier( + PhotoViewScaleState.initial, + )..addListener(_scaleStateChangeListener); final StreamController _outputScaleStateCtrl = StreamController.broadcast() ..sink.add(PhotoViewScaleState.initial); diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index e1ec36862a..944e5ba7e6 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -18,9 +18,7 @@ import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_gesture_det import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_hit_corners.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; -const _defaultDecoration = BoxDecoration( - color: Color.fromRGBO(0, 0, 0, 1.0), -); +const _defaultDecoration = BoxDecoration(color: Color.fromRGBO(0, 0, 0, 1.0)); /// Internal widget in which controls all animations lifecycle, core responses /// to user gestures, updates to the controller state and mounts the entire PhotoView Layout @@ -77,9 +75,9 @@ class PhotoViewCore extends StatefulWidget { required this.disableGestures, required this.disableScaleGestures, required this.enablePanAlways, - }) : semanticLabel = null, - imageProvider = null, - gaplessPlayback = false; + }) : semanticLabel = null, + imageProvider = null, + gaplessPlayback = false; final Decoration? backgroundDecoration; final ImageProvider? imageProvider; @@ -163,9 +161,9 @@ class PhotoViewCoreState extends State } bool _shouldAllowPanRotate() => switch (scaleStateController.scaleState) { - PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, - _ => true, - }; + PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, + _ => true, + }; void onScaleUpdate(ScaleUpdateDetails details) { final double newScale = _scaleBefore! * details.scale; @@ -206,10 +204,7 @@ class PhotoViewCoreState extends State if (s > maxScale) { final double scaleComebackRatio = maxScale / s; animateScale(s, maxScale); - final Offset clampedPosition = clampPosition( - position: p * scaleComebackRatio, - scale: maxScale, - ); + final Offset clampedPosition = clampPosition(position: p * scaleComebackRatio, scale: maxScale); animatePosition(p, clampedPosition); return; } @@ -218,13 +213,7 @@ class PhotoViewCoreState extends State if (s < minScale) { final double scaleComebackRatio = minScale / s; animateScale(s, minScale); - animatePosition( - p, - clampPosition( - position: p * scaleComebackRatio, - scale: minScale, - ), - ); + animatePosition(p, clampPosition(position: p * scaleComebackRatio, scale: minScale)); return; } // get magnitude from gesture velocity @@ -233,10 +222,7 @@ class PhotoViewCoreState extends State // animate velocity only if there is no scale change and a significant magnitude if (_scaleBefore! / s == 1.0 && magnitude >= 400.0) { final Offset direction = details.velocity.pixelsPerSecond / magnitude; - animatePosition( - p, - clampPosition(position: p + direction * 100.0), - ); + animatePosition(p, clampPosition(position: p + direction * 100.0)); } } @@ -248,10 +234,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _scaleAnimation = Tween( - begin: from, - end: to, - ).animate(_scaleAnimationController); + _scaleAnimation = Tween(begin: from, end: to).animate(_scaleAnimationController); _scaleAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -355,10 +338,7 @@ class PhotoViewCoreState extends State return StreamBuilder( stream: controller.outputStateStream, initialData: controller.prevValue, - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final PhotoViewControllerValue value = snapshot.data!; final useImageScale = widget.filterQuality != FilterQuality.none; @@ -371,11 +351,7 @@ class PhotoViewCoreState extends State ..rotateZ(value.rotation); final Widget customChildLayout = CustomSingleChildLayout( - delegate: _CenterWithOriginalSizeDelegate( - scaleBoundaries.childSize, - basePosition, - useImageScale, - ), + delegate: _CenterWithOriginalSizeDelegate(scaleBoundaries.childSize, basePosition, useImageScale), child: _buildHero(_buildChild()), ); @@ -383,11 +359,7 @@ class PhotoViewCoreState extends State constraints: widget.tightMode ? BoxConstraints.tight(scaleBoundaries.childSize * scale) : null, decoration: widget.backgroundDecoration ?? _defaultDecoration, child: Center( - child: Transform( - transform: matrix, - alignment: basePosition, - child: customChildLayout, - ), + child: Transform(transform: matrix, alignment: basePosition, child: customChildLayout), ), ); @@ -402,28 +374,20 @@ class PhotoViewCoreState extends State onScaleUpdate: widget.disableScaleGestures ? null : onScaleUpdate, onScaleEnd: widget.disableScaleGestures ? null : onScaleEnd, onDragStart: widget.onDragStart != null - ? (details) => widget.onDragStart!( - context, - details, - widget.controller, - widget.scaleStateController, - ) + ? (details) => widget.onDragStart!(context, details, widget.controller, widget.scaleStateController) : null, onDragEnd: widget.onDragEnd != null ? (details) => widget.onDragEnd!(context, details, widget.controller.value) : null, onDragUpdate: widget.onDragUpdate != null - ? (details) => widget.onDragUpdate!( - context, - details, - widget.controller.value, - ) + ? (details) => widget.onDragUpdate!(context, details, widget.controller.value) : null, hitDetector: this, onTapUp: widget.onTapUp != null ? (details) => widget.onTapUp!(context, details, value) : null, onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null, - onLongPressStart: - widget.onLongPressStart != null ? (details) => widget.onLongPressStart!(context, details, value) : null, + onLongPressStart: widget.onLongPressStart != null + ? (details) => widget.onLongPressStart!(context, details, value) + : null, child: child, ); } else { @@ -462,11 +426,7 @@ class PhotoViewCoreState extends State } class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { - const _CenterWithOriginalSizeDelegate( - this.subjectSize, - this.basePosition, - this.useImageScale, - ); + const _CenterWithOriginalSizeDelegate(this.subjectSize, this.basePosition, this.useImageScale); final Size subjectSize; final Alignment basePosition; diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart index 6f456713a9..7a5406c675 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart @@ -103,15 +103,13 @@ class PhotoViewGestureDetector extends StatelessWidget { ); gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(debugOwner: this), (LongPressGestureRecognizer instance) { - instance.onLongPressStart = onLongPressStart; - }); - - return RawGestureDetector( - behavior: behavior, - gestures: gestures, - child: child, + () => LongPressGestureRecognizer(debugOwner: this), + (LongPressGestureRecognizer instance) { + instance.onLongPressStart = onLongPressStart; + }, ); + + return RawGestureDetector(behavior: behavior, gestures: gestures, child: child); } } @@ -241,16 +239,11 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { /// ); /// ``` class PhotoViewGestureDetectorScope extends InheritedWidget { - const PhotoViewGestureDetectorScope({ - super.key, - this.axis, - this.touchSlopFactor = .2, - required super.child, - }); + const PhotoViewGestureDetectorScope({super.key, this.axis, this.touchSlopFactor = .2, required super.child}); static PhotoViewGestureDetectorScope? of(BuildContext context) { - final PhotoViewGestureDetectorScope? scope = - context.dependOnInheritedWidgetOfExactType(); + final PhotoViewGestureDetectorScope? scope = context + .dependOnInheritedWidgetOfExactType(); return scope; } @@ -273,10 +266,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { // we cannot change that, but we can prevent the scrollable from panning until this threshold is reached // and let other recognizers accept the gesture instead class PhotoViewPageViewScrollPhysics extends ScrollPhysics { - const PhotoViewPageViewScrollPhysics({ - this.touchSlopFactor = 0.1, - super.parent, - }); + const PhotoViewPageViewScrollPhysics({this.touchSlopFactor = 0.1, super.parent}); // in [0, 1] // 0: most reactive but will not let PhotoView recognizers accept gestures @@ -285,10 +275,7 @@ class PhotoViewPageViewScrollPhysics extends ScrollPhysics { @override PhotoViewPageViewScrollPhysics applyTo(ScrollPhysics? ancestor) { - return PhotoViewPageViewScrollPhysics( - touchSlopFactor: touchSlopFactor, - parent: buildParent(ancestor), - ); + return PhotoViewPageViewScrollPhysics(touchSlopFactor: touchSlopFactor, parent: buildParent(ancestor)); } @override diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart index b02b7feb68..eac0cb50ce 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart @@ -25,10 +25,7 @@ mixin HitCornersDetector on PhotoViewControllerDelegate { return HitCorners(y <= cornersY.min, y >= cornersY.max); } - bool _shouldMoveAxis( - HitCorners hitCorners, - double mainAxisMove, - ) { + bool _shouldMoveAxis(HitCorners hitCorners, double mainAxisMove) { if (mainAxisMove == 0) { return false; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart index 912fb5e839..fac0550c3b 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart @@ -9,13 +9,7 @@ class PhotoViewDefaultError extends StatelessWidget { Widget build(BuildContext context) { return DecoratedBox( decoration: decoration, - child: Center( - child: Icon( - Icons.broken_image, - color: Colors.grey[400], - size: 40.0, - ), - ), + child: Center(child: Icon(Icons.broken_image, color: Colors.grey[400], size: 40.0)), ); } } @@ -32,11 +26,7 @@ class PhotoViewDefaultLoading extends StatelessWidget { final value = loadedBytes != null && expectedBytes != null ? loadedBytes / expectedBytes : null; return Center( - child: SizedBox( - width: 20.0, - height: 20.0, - child: CircularProgressIndicator(value: value), - ), + child: SizedBox(width: 20.0, height: 20.0, child: CircularProgressIndicator(value: value)), ); } } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index d4afe85d2b..4f283e55fd 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -109,9 +109,7 @@ class _ImageWrapperState extends State { // retrieve image from the provider void _resolveImage() { - final ImageStream newStream = widget.imageProvider.resolve( - const ImageConfiguration(), - ); + final ImageStream newStream = widget.imageProvider.resolve(const ImageConfiguration()); _updateSourceStream(newStream); } @@ -125,10 +123,7 @@ class _ImageWrapperState extends State { void handleImageFrame(ImageInfo info, bool synchronousCall) { setupCB() { - _imageSize = Size( - info.image.width.toDouble(), - info.image.height.toDouble(), - ); + _imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble()); _loading = false; _imageInfo = _imageInfo; @@ -154,11 +149,7 @@ class _ImageWrapperState extends State { }()); } - _imageStreamListener = ImageStreamListener( - handleImageFrame, - onChunk: handleImageChunk, - onError: handleError, - ); + _imageStreamListener = ImageStreamListener(handleImageFrame, onChunk: handleImageChunk, onError: handleError); return _imageStreamListener!; } @@ -227,20 +218,14 @@ class _ImageWrapperState extends State { return widget.loadingBuilder!(context, _loadingProgress, widget.index); } - return PhotoViewDefaultLoading( - event: _loadingProgress, - ); + return PhotoViewDefaultLoading(event: _loadingProgress); } - Widget _buildError( - BuildContext context, - ) { + Widget _buildError(BuildContext context) { if (widget.errorBuilder != null) { return widget.errorBuilder!(context, _lastException!, _lastStack); } - return PhotoViewDefaultError( - decoration: widget.backgroundDecoration, - ); + return PhotoViewDefaultError(decoration: widget.backgroundDecoration); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart index da213903f6..3ca31cb8f9 100644 --- a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart +++ b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart @@ -58,11 +58,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { } } catch (exception, stack) { FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'Photoview library', - ), + FlutterErrorDetails(exception: exception, stack: stack, library: 'Photoview library'), ); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart index 1efdc50161..d120955250 100644 --- a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart +++ b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart @@ -5,22 +5,15 @@ import "package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.d import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart'; /// Given a [PhotoViewScaleState], returns a scale value considering [scaleBoundaries]. -double getScaleForScaleState( - PhotoViewScaleState scaleState, - ScaleBoundaries scaleBoundaries, -) { +double getScaleForScaleState(PhotoViewScaleState scaleState, ScaleBoundaries scaleBoundaries) { return switch (scaleState) { PhotoViewScaleState.initial || PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - _clampSize(scaleBoundaries.initialScale, scaleBoundaries), + PhotoViewScaleState.zoomedOut => _clampSize(scaleBoundaries.initialScale, scaleBoundaries), PhotoViewScaleState.covering => _clampSize( - _scaleForCovering( - scaleBoundaries.outerSize, - scaleBoundaries.childSize, - ), - scaleBoundaries, - ), + _scaleForCovering(scaleBoundaries.outerSize, scaleBoundaries.childSize), + scaleBoundaries, + ), PhotoViewScaleState.originalSize => _clampSize(1.0, scaleBoundaries), }; } @@ -28,13 +21,7 @@ double getScaleForScaleState( /// Internal class to wraps custom scale boundaries (min, max and initial) /// Also, stores values regarding the two sizes: the container and the child. class ScaleBoundaries { - const ScaleBoundaries( - this._minScale, - this._maxScale, - this._initialScale, - this.outerSize, - this.childSize, - ); + const ScaleBoundaries(this._minScale, this._maxScale, this._initialScale, this.outerSize, this.childSize); final dynamic _minScale; final dynamic _maxScale; diff --git a/mobile/lib/widgets/search/curated_people_row.dart b/mobile/lib/widgets/search/curated_people_row.dart index 10c19c7e60..74fc3e1c34 100644 --- a/mobile/lib/widgets/search/curated_people_row.dart +++ b/mobile/lib/widgets/search/curated_people_row.dart @@ -15,13 +15,7 @@ class CuratedPeopleRow extends StatelessWidget { final Function(SearchCuratedContent, int)? onTap; final Function(SearchCuratedContent, int)? onNameTap; - const CuratedPeopleRow({ - super.key, - required this.content, - this.onTap, - this.padding, - required this.onNameTap, - }); + const CuratedPeopleRow({super.key, required this.content, this.onTap, this.padding, required this.onNameTap}); @override Widget build(BuildContext context) { @@ -50,19 +44,13 @@ class CuratedPeopleRow extends StatelessWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), ), const SizedBox(height: 8), - SizedBox( - width: imageSize, - child: _buildPersonLabel(context, person, index), - ), + SizedBox(width: imageSize, child: _buildPersonLabel(context, person, index)), ], ), ); @@ -72,19 +60,13 @@ class CuratedPeopleRow extends StatelessWidget { ); } - Widget _buildPersonLabel( - BuildContext context, - SearchCuratedContent person, - int index, - ) { + Widget _buildPersonLabel(BuildContext context, SearchCuratedContent person, int index) { if (person.label.isEmpty) { return GestureDetector( onTap: () => onNameTap?.call(person, index), child: Text( "exif_bottom_sheet_person_add_person", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -101,11 +83,7 @@ class CuratedPeopleRow extends StatelessWidget { style: context.textTheme.labelLarge, maxLines: 2, ), - if (person.subtitle != null) - Text( - person.subtitle!, - textAlign: TextAlign.center, - ), + if (person.subtitle != null) Text(person.subtitle!, textAlign: TextAlign.center), ], ); } diff --git a/mobile/lib/widgets/search/curated_places_row.dart b/mobile/lib/widgets/search/curated_places_row.dart index 38092071d0..9d21292bde 100644 --- a/mobile/lib/widgets/search/curated_places_row.dart +++ b/mobile/lib/widgets/search/curated_places_row.dart @@ -31,9 +31,7 @@ class CuratedPlacesRow extends StatelessWidget { height: imageSize, child: ListView.separated( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), separatorBuilder: (context, index) => const SizedBox(width: 10), itemBuilder: (context, index) { // Injecting Map thumbnail as the first element diff --git a/mobile/lib/widgets/search/explore_grid.dart b/mobile/lib/widgets/search/explore_grid.dart index 1841f7f051..a6e1cf5aac 100644 --- a/mobile/lib/widgets/search/explore_grid.dart +++ b/mobile/lib/widgets/search/explore_grid.dart @@ -13,11 +13,7 @@ class ExploreGrid extends StatelessWidget { final List curatedContent; final bool isPeople; - const ExploreGrid({ - super.key, - required this.curatedContent, - this.isPeople = false, - }); + const ExploreGrid({super.key, required this.curatedContent, this.isPeople = false}); @override Widget build(BuildContext context) { @@ -27,10 +23,7 @@ class ExploreGrid extends StatelessWidget { child: SizedBox( height: 100, width: 100, - child: ThumbnailWithInfo( - textInfo: '', - onTap: () {}, - ), + child: ThumbnailWithInfo(textInfo: '', onTap: () {}), ), ); } @@ -53,26 +46,15 @@ class ExploreGrid extends StatelessWidget { borderRadius: 0, onTap: () { isPeople - ? context.pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + ? context.pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) : context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: content.label, - ), + location: SearchLocationFilter(city: content.label), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), diff --git a/mobile/lib/widgets/search/person_name_edit_form.dart b/mobile/lib/widgets/search/person_name_edit_form.dart index 886f17b2cc..d95d7c7483 100644 --- a/mobile/lib/widgets/search/person_name_edit_form.dart +++ b/mobile/lib/widgets/search/person_name_edit_form.dart @@ -16,11 +16,7 @@ class PersonNameEditForm extends HookConsumerWidget { final String personId; final String personName; - const PersonNameEditForm({ - super.key, - required this.personId, - required this.personName, - }); + const PersonNameEditForm({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,10 +24,7 @@ class PersonNameEditForm extends HookConsumerWidget { final isError = useState(false); return AlertDialog( - title: const Text( - "add_a_name", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("add_a_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SingleChildScrollView( child: TextFormField( controller: controller, @@ -46,23 +39,16 @@ class PersonNameEditForm extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: () => context.pop( - const PersonNameEditFormResult(false, ''), - ), + onPressed: () => context.pop(const PersonNameEditFormResult(false, '')), child: Text( "cancel", - style: TextStyle( - color: Colors.red[300], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () async { isError.value = false; - final result = await ref.read( - updatePersonNameProvider(personId, controller.text).future, - ); + final result = await ref.read(updatePersonNameProvider(personId, controller.text).future); isError.value = !result; if (result) { context.pop(PersonNameEditFormResult(true, controller.text)); @@ -70,10 +56,7 @@ class PersonNameEditForm extends HookConsumerWidget { }, child: Text( "save", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/search_filter/camera_picker.dart b/mobile/lib/widgets/search/search_filter/camera_picker.dart index a7c0bb89af..a5204c2fbc 100644 --- a/mobile/lib/widgets/search/search_filter/camera_picker.dart +++ b/mobile/lib/widgets/search/search_filter/camera_picker.dart @@ -21,30 +21,14 @@ class CameraPicker extends HookConsumerWidget { final selectedMake = useState(filter?.make); final selectedModel = useState(filter?.model); - final make = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraMake, - ), - ); + final make = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraMake)); - final models = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraModel, - make: selectedMake.value, - ), - ); + final models = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraModel, make: selectedMake.value)); final makeWidget = SearchDropdown( dropdownMenuEntries: switch (make) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('make').tr(), @@ -56,24 +40,14 @@ class CameraPicker extends HookConsumerWidget { } selectedMake.value = value.toString(); modelTextController.value = TextEditingValue.empty; - onSelect({ - 'make': selectedMake.value, - 'model': null, - }); + onSelect({'make': selectedMake.value, 'model': null}); }, ); final modelWidget = SearchDropdown( dropdownMenuEntries: switch (models) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('model').tr(), @@ -81,21 +55,12 @@ class CameraPicker extends HookConsumerWidget { leadingIcon: const Icon(Icons.camera), onSelected: (value) { selectedModel.value = value.toString(); - onSelect({ - 'make': selectedMake.value, - 'model': selectedModel.value, - }); + onSelect({'make': selectedMake.value, 'model': selectedModel.value}); }, ); if (context.isMobile) { - return Column( - children: [ - makeWidget, - const SizedBox(height: 8), - modelWidget, - ], - ); + return Column(children: [makeWidget, const SizedBox(height: 8), modelWidget]); } return Row( diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index dd0ec44e45..70cbfd2c15 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -20,9 +20,7 @@ class SearchDropdown extends StatelessWidget { Widget build(BuildContext context) { final menuStyle = const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ); diff --git a/mobile/lib/widgets/search/search_filter/display_option_picker.dart b/mobile/lib/widgets/search/search_filter/display_option_picker.dart index 5deed5fe1b..a64eab7b71 100644 --- a/mobile/lib/widgets/search/search_filter/display_option_picker.dart +++ b/mobile/lib/widgets/search/search_filter/display_option_picker.dart @@ -3,18 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; -enum DisplayOption { - notInAlbum, - favorite, - archive, -} +enum DisplayOption { notInAlbum, favorite, archive } class DisplayOptionPicker extends HookWidget { - const DisplayOptionPicker({ - super.key, - required this.onSelect, - this.filter, - }); + const DisplayOptionPicker({super.key, required this.onSelect, this.filter}); final Function(Map) onSelect; final SearchDisplayFilters? filter; @@ -34,10 +26,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('search_filter_display_option_not_in_album').tr(), value: options.value[DisplayOption.notInAlbum], onChanged: (bool? value) { - options.value = { - ...options.value, - DisplayOption.notInAlbum: value!, - }; + options.value = {...options.value, DisplayOption.notInAlbum: value!}; onSelect(options.value); }, ), @@ -45,10 +34,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('favorite').tr(), value: options.value[DisplayOption.favorite], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.favorite: value!, - }; + options.value = {...options.value, DisplayOption.favorite: value!}; onSelect(options.value); }, ), @@ -56,10 +42,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('archive').tr(), value: options.value[DisplayOption.archive], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.archive: value!, - }; + options.value = {...options.value, DisplayOption.archive: value!}; onSelect(options.value); }, ), diff --git a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart index f534b94256..e8226b5b3a 100644 --- a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart +++ b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart @@ -33,10 +33,7 @@ class FilterBottomSheetScaffold extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Text( - title, - style: context.textTheme.headlineSmall, - ), + child: Text(title, style: context.textTheme.headlineSmall), ), buildChildWidget(), Padding( diff --git a/mobile/lib/widgets/search/search_filter/location_picker.dart b/mobile/lib/widgets/search/search_filter/location_picker.dart index eea4b52256..608183a2f6 100644 --- a/mobile/lib/widgets/search/search_filter/location_picker.dart +++ b/mobile/lib/widgets/search/search_filter/location_picker.dart @@ -52,14 +52,7 @@ class LocationPicker extends HookConsumerWidget { SearchDropdown( dropdownMenuEntries: switch (countries) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('country').tr(), @@ -71,27 +64,14 @@ class LocationPicker extends HookConsumerWidget { selectedCountry.value = value.toString(); stateTextController.value = TextEditingValue.empty; cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': null, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': null, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (states) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('state').tr(), @@ -102,38 +82,21 @@ class LocationPicker extends HookConsumerWidget { } selectedState.value = value.toString(); cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (cities) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('city').tr(), controller: cityTextController, onSelected: (value) { selectedCity.value = value.toString(); - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': selectedCity.value, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': selectedCity.value}); }, ), ], diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index 991664dd93..b2a7a18c7c 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -40,10 +40,7 @@ class PeoplePicker extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - thickness: 1, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest, thickness: 1), ), Expanded( child: people.widgetWhen( @@ -51,16 +48,12 @@ class PeoplePicker extends HookConsumerWidget { return ListView.builder( shrinkWrap: true, itemCount: people - .where( - (person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final person = people - .where( - (person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .toList()[index]; final isSelected = selectedPeople.value.contains(person); @@ -82,10 +75,7 @@ class PeoplePicker extends HookConsumerWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), diff --git a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart index 60d1366fbd..a72b4668dd 100644 --- a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart +++ b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart @@ -7,13 +7,7 @@ class SearchFilterChip extends StatelessWidget { final Widget? currentFilter; final IconData icon; - const SearchFilterChip({ - super.key, - required this.label, - required this.onTap, - required this.icon, - this.currentFilter, - }); + const SearchFilterChip({super.key, required this.label, required this.onTap, required this.icon, this.currentFilter}); @override Widget build(BuildContext context) { @@ -23,21 +17,10 @@ class SearchFilterChip extends StatelessWidget { child: Card( elevation: 0, color: context.primaryColor.withValues(alpha: .5), - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.secondaryContainer), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.secondaryContainer)), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - currentFilter!, - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), currentFilter!]), ), ), ); @@ -46,21 +29,10 @@ class SearchFilterChip extends StatelessWidget { onTap: onTap, child: Card( elevation: 0, - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.outline.withAlpha(15)), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.outline.withAlpha(15))), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - Text(label), - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), Text(label)]), ), ), ); diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index 78af8f936b..7533e46f1a 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart' import 'package:maplibre_gl/maplibre_gl.dart'; class SearchMapThumbnail extends StatelessWidget { - const SearchMapThumbnail({ - super.key, - this.size = 60.0, - }); + const SearchMapThumbnail({super.key, this.size = 60.0}); final double size; final bool showTitle = true; @@ -23,16 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { context.pushRoute(MapRoute()); }, child: IgnorePointer( - child: MapThumbnail( - zoom: 2, - centre: const LatLng( - 47, - 5, - ), - height: size, - width: size, - showAttribution: false, - ), + child: MapThumbnail(zoom: 2, centre: const LatLng(47, 5), height: size, width: size, showAttribution: false), ), ); } diff --git a/mobile/lib/widgets/search/search_row_section.dart b/mobile/lib/widgets/search/search_row_section.dart index 352c7f6a40..b8584fefef 100644 --- a/mobile/lib/widgets/search/search_row_section.dart +++ b/mobile/lib/widgets/search/search_row_section.dart @@ -25,10 +25,7 @@ class SearchRowSection extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: SearchRowTitle( - onViewAllPressed: onViewAllPressed, - title: title, - ), + child: SearchRowTitle(onViewAllPressed: onViewAllPressed, title: title), ), child, ], diff --git a/mobile/lib/widgets/search/search_row_title.dart b/mobile/lib/widgets/search/search_row_title.dart index 4fa0d1f854..dc0a4ba6cb 100644 --- a/mobile/lib/widgets/search/search_row_title.dart +++ b/mobile/lib/widgets/search/search_row_title.dart @@ -3,11 +3,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SearchRowTitle extends StatelessWidget { - const SearchRowTitle({ - super.key, - required this.onViewAllPressed, - required this.title, - }); + const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title}); final Function() onViewAllPressed; final String title; @@ -17,19 +13,12 @@ class SearchRowTitle extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), TextButton( onPressed: onViewAllPressed, child: Text( 'search_page_view_all_button', - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/thumbnail_with_info.dart b/mobile/lib/widgets/search/thumbnail_with_info.dart index 23bdbc915b..af9460f929 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info.dart @@ -39,12 +39,7 @@ class ThumbnailWithInfo extends StatelessWidget { errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ) - : Center( - child: Icon( - noImageIcon ?? Icons.not_listed_location, - color: textAndIconColor, - ), - ), + : Center(child: Icon(noImageIcon ?? Icons.not_listed_location, color: textAndIconColor)), ); } } diff --git a/mobile/lib/widgets/search/thumbnail_with_info_container.dart b/mobile/lib/widgets/search/thumbnail_with_info_container.dart index e29d9e780c..e4b8f69f83 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info_container.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info_container.dart @@ -27,10 +27,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), gradient: LinearGradient( - colors: [ - context.colorScheme.surfaceContainer, - context.colorScheme.surfaceContainer.darken(amount: .1), - ], + colors: [context.colorScheme.surfaceContainer, context.colorScheme.surfaceContainer.darken(amount: .1)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -54,11 +51,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8) + const EdgeInsets.only(bottom: 8), child: Text( label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14), maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 3f569863de..3f196b840b 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -34,10 +34,7 @@ class AdvancedSettings extends HookConsumerWidget { final logLevel = Level.LEVELS[levelId.value].name; - useValueChanged( - levelId.value, - (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel()), - ); + useValueChanged(levelId.value, (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel())); Future checkAndroidVersion() async { if (Platform.isAndroid) { diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index df974fff30..04786bf916 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; class GroupSettings extends HookConsumerWidget { - const GroupSettings({ - super.key, - }); + const GroupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,10 +19,7 @@ class GroupSettings extends HookConsumerWidget { final groupBy = GroupAssetsBy.values[groupByIndex.value]; Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.groupAssetsBy, - groupBy.index, - ); + await ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.groupAssetsBy, groupBy.index); ref.invalidate(appSettingsServiceProvider); } @@ -41,18 +36,9 @@ class GroupSettings extends HookConsumerWidget { SettingsSubTitle(title: "asset_list_group_by_sub_title".tr()), SettingsRadioListTile( groups: [ - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_by_month_day'.tr(), - value: GroupAssetsBy.day, - ), - SettingsRadioGroup( - title: 'month'.tr(), - value: GroupAssetsBy.month, - ), - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_automatically'.tr(), - value: GroupAssetsBy.auto, - ), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_by_month_day'.tr(), value: GroupAssetsBy.day), + SettingsRadioGroup(title: 'month'.tr(), value: GroupAssetsBy.month), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_automatically'.tr(), value: GroupAssetsBy.auto), ], groupBy: groupBy, onRadioChanged: changeGroupValue, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index de8ae5c2b2..bcb4a5ec9c 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class LayoutSettings extends HookConsumerWidget { - const LayoutSettings({ - super.key, - }); + const LayoutSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index 550e3b5165..9f0ed0aa87 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -10,9 +10,7 @@ import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'asset_list_layout_settings.dart'; class AssetListSettings extends HookConsumerWidget { - const AssetListSettings({ - super.key, - }); + const AssetListSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,9 +26,6 @@ class AssetListSettings extends HookConsumerWidget { const GroupSettings(), ]; - return SettingsSubPageScaffold( - settings: assetListSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetListSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart index 23dca85b6f..5dea38d85e 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'video_viewer_settings.dart'; class AssetViewerSettings extends StatelessWidget { - const AssetViewerSettings({ - super.key, - }); + const AssetViewerSettings({super.key}); @override Widget build(BuildContext context) { - final assetViewerSetting = [ - const ImageViewerQualitySetting(), - const VideoViewerSettings(), - ]; + final assetViewerSetting = [const ImageViewerQualitySetting(), const VideoViewerSettings()]; - return SettingsSubPageScaffold( - settings: assetViewerSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetViewerSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index 444977d9a2..aed88b90b0 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ImageViewerQualitySetting extends HookConsumerWidget { - const ImageViewerQualitySetting({ - super.key, - }); + const ImageViewerQualitySetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,10 +22,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget { SettingsSubTitle(title: "setting_image_viewer_title".tr()), ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), - title: Text( - 'setting_image_viewer_help', - style: context.textTheme.bodyMedium, - ).tr(), + title: Text('setting_image_viewer_help', style: context.textTheme.bodyMedium).tr(), ), SettingsSwitchListTile( valueNotifier: isPreview, diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 66f7e96943..1d8d9812be 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -8,9 +8,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class VideoViewerSettings extends HookConsumerWidget { - const VideoViewerSettings({ - super.key, - }); + const VideoViewerSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index 5207969045..038a567dc2 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -24,12 +24,7 @@ class BackgroundBackupSettings extends ConsumerWidget { void showErrorToUser(String msg) { final snackBar = SnackBar( - content: Text( - msg.tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), - ), + content: Text(msg.tr(), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor)), backgroundColor: Colors.red, ); context.scaffoldMessenger.showSnackBar(snackBar); @@ -41,20 +36,14 @@ class BackgroundBackupSettings extends ConsumerWidget { barrierDismissible: false, builder: (BuildContext ctx) { return AlertDialog( - title: const Text( - 'backup_controller_page_background_battery_info_title', - ).tr(), + title: const Text('backup_controller_page_background_battery_info_title').tr(), content: SingleChildScrollView( - child: const Text( - 'backup_controller_page_background_battery_info_message', - ).tr(), + child: const Text('backup_controller_page_background_battery_info_message').tr(), ), actions: [ ElevatedButton( - onPressed: () => launchUrl( - Uri.parse('https://dontkillmyapp.com'), - mode: LaunchMode.externalApplication, - ), + onPressed: () => + launchUrl(Uri.parse('https://dontkillmyapp.com'), mode: LaunchMode.externalApplication), child: const Text( "backup_controller_page_background_battery_info_link", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), @@ -79,7 +68,9 @@ class BackgroundBackupSettings extends ConsumerWidget { title: 'backup_controller_page_background_is_off'.tr(), subtileText: 'backup_controller_page_background_description'.tr(), buttonText: 'backup_controller_page_background_turn_on'.tr(), - onButtonTap: () => ref.read(backupProvider.notifier).configureBackgroundBackup( + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( enabled: true, onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser, @@ -90,10 +81,7 @@ class BackgroundBackupSettings extends ConsumerWidget { return Column( children: [ if (!Platform.isIOS || iosSettings?.appRefreshEnabled == true) - _BackgroundSettingsEnabled( - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), + _BackgroundSettingsEnabled(onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser), if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) const _IOSBackgroundRefreshDisabled(), if (Platform.isIOS && iosSettings != null) IosDebugInfoTile(settings: iosSettings), ], @@ -120,10 +108,7 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final void Function(String msg) onError; final void Function() onBatteryInfo; - const _BackgroundSettingsEnabled({ - required this.onError, - required this.onBatteryInfo, - }); + const _BackgroundSettingsEnabled({required this.onError, required this.onBatteryInfo}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -131,41 +116,45 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final isWifiRequiredNotifier = useValueNotifier(isWifiRequired); useValueChanged( isWifiRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isWifiRequiredNotifier.value = isWifiRequired, - ), + (_, __) => WidgetsBinding.instance.addPostFrameCallback((_) => isWifiRequiredNotifier.value = isWifiRequired), ); final isChargingRequired = ref.watch(backupProvider.select((s) => s.backupRequireCharging)); final isChargingRequiredNotifier = useValueNotifier(isChargingRequired); useValueChanged( isChargingRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isChargingRequiredNotifier.value = isChargingRequired, - ), + (_, __) => + WidgetsBinding.instance.addPostFrameCallback((_) => isChargingRequiredNotifier.value = isChargingRequired), ); int backupDelayToSliderValue(int ms) => switch (ms) { - 5000 => 0, - 30000 => 1, - 120000 => 2, - _ => 3, - }; + 5000 => 0, + 30000 => 1, + 120000 => 2, + _ => 3, + }; - int backupDelayToMilliseconds(int v) => switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 }; + int backupDelayToMilliseconds(int v) => switch (v) { + 0 => 5000, + 1 => 30000, + 2 => 120000, + _ => 600000, + }; String formatBackupDelaySliderValue(int v) => switch (v) { - 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), - 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), - 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), - _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), - }; + 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), + }; final backupTriggerDelay = ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); final triggerDelay = useState(backupDelayToSliderValue(backupTriggerDelay)); useValueChanged( triggerDelay.value, - (_, __) => ref.read(backupProvider.notifier).configureBackgroundBackup( + (_, __) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( triggerDelay: backupDelayToMilliseconds(triggerDelay.value), onError: onError, onBatteryInfo: onBatteryInfo, @@ -177,40 +166,32 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { iconColor: context.primaryColor, title: 'backup_controller_page_background_is_on'.tr(), buttonText: 'backup_controller_page_background_turn_off'.tr(), - onButtonTap: () => ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onError: onError, onBatteryInfo: onBatteryInfo), subtitle: Column( children: [ SettingsSwitchListTile( valueNotifier: isWifiRequiredNotifier, title: 'backup_controller_page_background_wifi'.tr(), icon: Icons.wifi, - onChanged: (enabled) => ref.read(backupProvider.notifier).configureBackgroundBackup( - requireWifi: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireWifi: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), SettingsSwitchListTile( valueNotifier: isChargingRequiredNotifier, title: 'backup_controller_page_background_charging'.tr(), icon: Icons.charging_station, - onChanged: (enabled) => ref.read(backupProvider.notifier).configureBackgroundBackup( - requireCharging: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireCharging: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), if (Platform.isAndroid) SettingsSliderListTile( valueNotifier: triggerDelay, text: 'backup_controller_page_background_delay'.tr( - namedArgs: { - 'duration': formatBackupDelaySliderValue(triggerDelay.value), - }, + namedArgs: {'duration': formatBackupDelaySliderValue(triggerDelay.value)}, ), maxValue: 3.0, noDivisons: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart index 59ea6f2105..50aa57da9f 100644 --- a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart @@ -15,9 +15,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class BackupSettings extends HookConsumerWidget { - const BackupSettings({ - super.key, - }); + const BackupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -71,22 +69,14 @@ class BackupSettings extends HookConsumerWidget { SettingsButtonListTile( icon: Icons.photo_album_outlined, title: 'sync_albums'.tr(), - subtitle: Text( - "sync_albums_manual_subtitle".tr(), - ), + subtitle: Text("sync_albums_manual_subtitle".tr()), buttonText: 'sync_albums'.tr(), child: isAlbumSyncInProgress.value ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: syncAlbums, - child: Text('sync'.tr()), - ), + : ElevatedButton(onPressed: syncAlbums, child: Text('sync'.tr())), ), ]; - return SettingsSubPageScaffold( - settings: backupSettings, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: backupSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 85da49357b..12a3c51e8e 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -17,9 +17,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; class BetaSyncSettings extends HookConsumerWidget { - const BetaSyncSettings({ - super.key, - }); + const BetaSyncSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,17 +33,11 @@ class BetaSyncSettings extends HookConsumerWidget { final memoryCount = memoryService.getCount(); final getLocalHashedCount = assetService.getLocalHashedCount(); - return await Future.wait([ - assetCounts, - localAlbumCounts, - remoteAlbumCounts, - memoryCount, - getLocalHashedCount, - ]); + return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); } Future resetDatabase() async { -// https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 final drift = ref.read(driftProvider); final database = drift.attachedDatabase; await database.exclusively(() async { @@ -62,14 +54,10 @@ class BetaSyncSettings extends HookConsumerWidget { database.resolvedEngine.executor, drift_db.OpeningDetails(null, database.schemaVersion), ); - await database.customStatement( - 'PRAGMA user_version = ${database.schemaVersion}', - ); + await database.customStatement('PRAGMA user_version = ${database.schemaVersion}'); // Refresh all stream queries - database.notifyUpdates({ - for (final table in database.allTables) drift_db.TableUpdate.onTable(table), - }); + database.notifyUpdates({for (final table in database.allTables) drift_db.TableUpdate.onTable(table)}); }); } @@ -83,28 +71,18 @@ class BetaSyncSettings extends HookConsumerWidget { if (!await dbFile.exists()) { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Database file not found".t(context: context)), - ), + SnackBar(content: Text("Database file not found".t(context: context))), ); } return; } final timestamp = DateTime.now().millisecondsSinceEpoch; - final exportFile = File( - path.join( - documentsDir.path, - 'immich_export_$timestamp.sqlite', - ), - ); + final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); await dbFile.copy(exportFile.path); - await Share.shareXFiles( - [XFile(exportFile.path)], - text: 'Immich Database Export', - ); + await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); Future.delayed(const Duration(seconds: 30), () async { if (await exportFile.exists()) { @@ -114,17 +92,13 @@ class BetaSyncSettings extends HookConsumerWidget { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Database exported successfully".t(context: context)), - ), + SnackBar(content: Text("Database exported successfully".t(context: context))), ); } } catch (e) { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Failed to export database: $e".t(context: context)), - ), + SnackBar(content: Text("Failed to export database: $e".t(context: context))), ); } } @@ -225,27 +199,17 @@ class BetaSyncSettings extends HookConsumerWidget { ], ), ), - const Divider( - height: 1, - indent: 16, - endIndent: 16, - ), + const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "jobs".t(context: context)), ListTile( title: Text( "sync_local".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "tap_to_run_job".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("tap_to_run_job".t(context: context)), leading: const Icon(Icons.sync), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).localSyncStatus, - ), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncLocal(full: true); }, @@ -253,17 +217,11 @@ class BetaSyncSettings extends HookConsumerWidget { ListTile( title: Text( "sync_remote".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "tap_to_run_job".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("tap_to_run_job".t(context: context)), leading: const Icon(Icons.cloud_sync), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).remoteSyncStatus, - ), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncRemote(); }, @@ -271,64 +229,40 @@ class BetaSyncSettings extends HookConsumerWidget { ListTile( title: Text( "hash_asset".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontWeight: FontWeight.w500), ), leading: const Icon(Icons.tag), - subtitle: Text( - "tap_to_run_job".t(context: context), - ), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).hashJobStatus, - ), + subtitle: Text("tap_to_run_job".t(context: context)), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), onTap: () { ref.read(backgroundSyncProvider).hashAssets(); }, ), - const Divider( - height: 1, - indent: 16, - endIndent: 16, - ), + const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), ListTile( title: Text( "export_database".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "export_database_description".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("export_database_description".t(context: context)), leading: const Icon(Icons.download), onTap: exportDatabase, ), ListTile( title: Text( "reset_sqlite".t(context: context), - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.w500, - ), - ), - leading: Icon( - Icons.settings_backup_restore_rounded, - color: context.colorScheme.error, + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), ), + leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), onTap: () async { showDialog( context: context, builder: (context) { return AlertDialog( - title: Text( - "reset_sqlite".t(context: context), - ), - content: Text( - "reset_sqlite_confirmation".t(context: context), - ), + title: Text("reset_sqlite".t(context: context)), + content: Text("reset_sqlite_confirmation".t(context: context)), actions: [ TextButton( onPressed: () => context.pop(), @@ -339,18 +273,12 @@ class BetaSyncSettings extends HookConsumerWidget { await resetDatabase(); context.pop(); context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text( - "reset_sqlite_success".t(context: context), - ), - ), + SnackBar(content: Text("reset_sqlite_success".t(context: context))), ); }, child: Text( "confirm".t(context: context), - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ], @@ -370,31 +298,15 @@ class BetaSyncSettings extends HookConsumerWidget { class _SyncStatusIcon extends StatelessWidget { final SyncStatus status; - const _SyncStatusIcon({ - required this.status, - }); + const _SyncStatusIcon({required this.status}); @override Widget build(BuildContext context) { return switch (status) { - SyncStatus.idle => const Icon( - Icons.pause_circle_outline_rounded, - ), - SyncStatus.syncing => const SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - SyncStatus.success => const Icon( - Icons.check_circle_outline, - color: Colors.green, - ), - SyncStatus.error => Icon( - Icons.error_outline, - color: context.colorScheme.error, - ), + SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), + SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), + SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), + SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), }; } } @@ -402,9 +314,7 @@ class _SyncStatusIcon extends StatelessWidget { class _SectionHeaderText extends StatelessWidget { final String text; - const _SectionHeaderText({ - required this.text, - }); + const _SectionHeaderText({required this.text}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart index 2441e7d1ca..ac357c2dee 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -7,12 +7,7 @@ class EntitiyCountTile extends StatelessWidget { final String label; final IconData icon; - const EntitiyCountTile({ - super.key, - required this.count, - required this.label, - required this.icon, - }); + const EntitiyCountTile({super.key, required this.count, required this.label, required this.icon}); String zeroPadding(int number, int targetWidth) { final numStr = number.toString(); @@ -31,10 +26,7 @@ class EntitiyCountTile extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainerLow, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - width: 0.5, - color: context.colorScheme.outline.withAlpha(25), - ), + border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)), ), child: Column( mainAxisSize: MainAxisSize.min, @@ -44,18 +36,11 @@ class EntitiyCountTile extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Icon( - icon, - color: context.primaryColor, - ), + Icon(icon, color: context.primaryColor), const SizedBox(width: 8), Text( label, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), ), ], ), @@ -66,23 +51,15 @@ class EntitiyCountTile extends StatelessWidget { final maxDigits = calculateMaxDigits(constraints.maxWidth); return RichText( text: TextSpan( - style: const TextStyle( - fontSize: 18, - fontFamily: 'OverpassMono', - fontWeight: FontWeight.w600, - ), + style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600), children: [ TextSpan( text: zeroPadding(count, maxDigits), - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary.withAlpha(75), - ), + style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), ), TextSpan( text: count.toString(), - style: TextStyle( - color: context.primaryColor, - ), + style: TextStyle(color: context.primaryColor), ), ], ), diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index f5f6d66898..3d41094b76 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -13,9 +13,7 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; class BetaTimelineListTile extends ConsumerStatefulWidget { - const BetaTimelineListTile({ - super.key, - }); + const BetaTimelineListTile({super.key}); @override ConsumerState createState() => _BetaTimelineListTileState(); @@ -30,31 +28,22 @@ class _BetaTimelineListTileState extends ConsumerState wit @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 3), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 3), vsync: this); - _rotationAnimation = Tween(begin: 0, end: 2 * math.pi).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.linear, - ), - ); + _rotationAnimation = Tween( + begin: 0, + end: 2 * math.pi, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear)); - _pulseAnimation = Tween(begin: 1, end: 1.1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _pulseAnimation = Tween( + begin: 1, + end: 1.1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _animationController.repeat(reverse: true); } @@ -85,12 +74,8 @@ class _BetaTimelineListTileState extends ConsumerState wit return AlertDialog( title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), content: value - ? const Text( - "Are you sure you want to enable the beta timeline?", - ) - : const Text( - "Are you sure you want to disable the beta timeline?", - ), + ? const Text("Are you sure you want to enable the beta timeline?") + : const Text("Are you sure you want to disable the beta timeline?"), actions: [ TextButton( onPressed: () { @@ -98,27 +83,16 @@ class _BetaTimelineListTileState extends ConsumerState wit }, child: Text( "cancel".t(context: context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.colorScheme.outline, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), ), ), ElevatedButton( onPressed: () async { Navigator.of(context).pop(); - await ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.betaTimeline, - value, - ); - context.router.replaceAll( - [ChangeExperienceRoute(switchingToBeta: value)], - ); + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.betaTimeline, value); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); }, - child: Text( - "ok".t(context: context), - ), + child: Text("ok".t(context: context)), ), ], ); @@ -156,11 +130,7 @@ class _BetaTimelineListTileState extends ConsumerState wit transform: GradientRotation(_rotationAnimation.value * 0.5), ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 8, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, 2)), ], ), child: Container( @@ -193,11 +163,7 @@ class _BetaTimelineListTileState extends ConsumerState wit ], ), ), - child: Icon( - Icons.auto_awesome, - color: context.primaryColor, - size: 20, - ), + child: Icon(Icons.auto_awesome, color: context.primaryColor, size: 20), ), ), ), @@ -211,20 +177,13 @@ class _BetaTimelineListTileState extends ConsumerState wit children: [ Text( "advanced_settings_beta_timeline_title".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(width: 8), Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 2, - ), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), + borderRadius: const BorderRadius.all(Radius.circular(8)), gradient: LinearGradient( colors: [ context.primaryColor.withValues(alpha: 0.8), diff --git a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart index 2e1f165602..f0e248b39d 100644 --- a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart +++ b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart @@ -15,15 +15,11 @@ class CustomeProxyHeaderSettings extends StatelessWidget { dense: true, title: Text( "headers_settings_tile_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), subtitle: Text( "headers_settings_tile_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(const HeaderSettingsRoute()), ); diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 012c966b60..d0b3ac0021 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -48,9 +48,7 @@ class LanguageSettings extends HookConsumerWidget { filteredLocaleEntries.value = localeEntries; } else { filteredLocaleEntries.value = localeEntries - .where( - (entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase()), - ) + .where((entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase())) .toList(); } }); @@ -61,17 +59,14 @@ class LanguageSettings extends HookConsumerWidget { onSearch(''); } - useEffect( - () { - void searchListener() => onSearch(searchController.text); - searchController.addListener(searchListener); - return () { - searchController.removeListener(searchListener); - debounceTimer.value?.cancel(); - }; - }, - [searchController], - ); + useEffect(() { + void searchListener() => onSearch(searchController.text); + searchController.addListener(searchListener); + return () { + searchController.removeListener(searchListener); + debounceTimer.value?.cancel(); + }; + }, [searchController]); return SafeArea( child: Column( @@ -111,11 +106,7 @@ class LanguageSettings extends HookConsumerWidget { _LanguageApplyButton( isDisabled: isButtonDisabled, isLoading: isLoading.value, - onPressed: () => _applyLanguageChange( - context, - selectedLocale, - isLoading, - ), + onPressed: () => _applyLanguageChange(context, selectedLocale, isLoading), ), ], ), @@ -140,9 +131,7 @@ class _LanguageSearchBar extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(top: 16, bottom: 8, left: 50, right: 50), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(24)), @@ -162,10 +151,7 @@ class _LanguageSearchBar extends StatelessWidget { hintText: 'language_search_hint'.t(context: context), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: controller.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClear, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClear) : null, controller: controller, onChanged: onChanged, @@ -186,24 +172,16 @@ class _LanguageNotFound extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.search_off_rounded, - size: 64, - color: context.colorScheme.onSurface.withValues(alpha: 0.4), - ), + Icon(Icons.search_off_rounded, size: 64, color: context.colorScheme.onSurface.withValues(alpha: 0.4)), const SizedBox(height: 8), Text( 'language_no_results_title'.t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface), ), const SizedBox(height: 4), Text( 'language_no_results_subtitle'.t(context: context), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.8), - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.8)), ), ], ), @@ -212,11 +190,7 @@ class _LanguageNotFound extends StatelessWidget { } class _LanguageApplyButton extends StatelessWidget { - const _LanguageApplyButton({ - required this.isDisabled, - required this.isLoading, - required this.onPressed, - }); + const _LanguageApplyButton({required this.isDisabled, required this.isLoading, required this.onPressed}); final bool isDisabled; final bool isLoading; @@ -225,9 +199,7 @@ class _LanguageApplyButton extends StatelessWidget { @override Widget build(BuildContext context) { return DecoratedBox( - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( @@ -236,18 +208,10 @@ class _LanguageApplyButton extends StatelessWidget { child: ElevatedButton( onPressed: isDisabled ? null : onPressed, child: isLoading - ? const SizedBox.square( - dimension: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) + ? const SizedBox.square(dimension: 24, child: CircularProgressIndicator(strokeWidth: 2)) : Text( 'setting_languages_apply'.t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16.0, - ), + style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0), ), ), ), @@ -273,20 +237,12 @@ class _LanguageItem extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), child: DecoratedBox( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), - border: Border.all( - color: context.colorScheme.outlineVariant.withValues(alpha: .4), - width: 1.0, - ), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + border: Border.all(color: context.colorScheme.outlineVariant.withValues(alpha: .4), width: 1.0), ), child: ListTile( title: Text( @@ -296,22 +252,12 @@ class _LanguageItem extends StatelessWidget { color: isSelected ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant, ), ), - trailing: isSelected - ? Icon( - Icons.check, - color: context.colorScheme.primary, - size: 20, - ) - : null, + trailing: isSelected ? Icon(Icons.check, color: context.colorScheme.primary, size: 20) : null, onTap: onTap, selected: isSelected, selectedTileColor: context.colorScheme.primary.withValues(alpha: .15), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0))), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), ), ), ); diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart index 06db78cb97..af9e4079bb 100644 --- a/mobile/lib/widgets/settings/local_storage_settings.dart +++ b/mobile/lib/widgets/settings/local_storage_settings.dart @@ -14,13 +14,10 @@ class LocalStorageSettings extends HookConsumerWidget { final isarDb = ref.watch(dbProvider); final cacheItemCount = useState(0); - useEffect( - () { - cacheItemCount.value = isarDb.duplicatedAssets.countSync(); - return null; - }, - [], - ); + useEffect(() { + cacheItemCount.value = isarDb.duplicatedAssets.countSync(); + return null; + }, []); void clearCache() async { await isarDb.writeTxn(() => isarDb.duplicatedAssets.clear()); @@ -32,15 +29,11 @@ class LocalStorageSettings extends HookConsumerWidget { dense: true, title: Text( "cache_settings_duplicated_assets_title", - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(namedArgs: {'count': "${cacheItemCount.value}"}), subtitle: Text( "cache_settings_duplicated_assets_subtitle", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), trailing: TextButton( onPressed: cacheItemCount.value > 0 ? clearCache : null, diff --git a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart index ec0069f2d1..a712ce416c 100644 --- a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart +++ b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart @@ -97,10 +97,7 @@ class EndpointInputState extends ConsumerState { color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 16), - child: const Icon( - Icons.delete, - color: Colors.white, - ), + child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 24), @@ -120,27 +117,19 @@ class EndpointInputState extends ConsumerState { autovalidateMode: AutovalidateMode.onUserInteraction, validator: validateUrl, keyboardType: TextInputType.url, - style: const TextStyle( - fontFamily: 'Inconsolata', - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: const TextStyle(fontFamily: 'Inconsolata', fontWeight: FontWeight.w600, fontSize: 14), decoration: InputDecoration( hintText: 'http(s)://immich.domain.com', contentPadding: const EdgeInsets.all(16), filled: true, fillColor: context.colorScheme.surfaceContainer, - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(16))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.red[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!, - ), + borderSide: BorderSide(color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index d0c212adf5..8cc6079961 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -17,9 +17,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final entries = useState( - [const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)], - ); + final entries = useState([const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)]); final canSave = useState(false); saveEndpointList() { @@ -29,10 +27,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { final jsonString = jsonEncode(endpointList); - Store.put( - StoreKey.externalEndpointList, - jsonString, - ); + Store.put(StoreKey.externalEndpointList, jsonString); } updateValidationStatus(String url, int index, AuxCheckStatus status) { @@ -59,11 +54,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { saveEndpointList(); } - Widget proxyDecorator( - Widget child, - int index, - Animation animation, - ) { + Widget proxyDecorator(Widget child, int index, Animation animation) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget? child) { @@ -77,20 +68,17 @@ class ExternalNetworkPreference extends HookConsumerWidget { ); } - useEffect( - () { - final jsonString = Store.tryGet(StoreKey.externalEndpointList); + useEffect(() { + final jsonString = Store.tryGet(StoreKey.externalEndpointList); - if (jsonString == null) { - return null; - } - - final List jsonList = jsonDecode(jsonString); - entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + if (jsonString == null) { return null; - }, - const [], - ); + } + + final List jsonList = jsonDecode(jsonString); + entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + return null; + }, const []); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -99,21 +87,14 @@ class ExternalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.dns_rounded, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.dns_rounded, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -121,14 +102,8 @@ class ExternalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "external_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("external_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), Divider(color: context.colorScheme.surfaceContainerHighest), @@ -165,10 +140,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { ? () { entries.value = [ ...entries.value, - const AuxilaryEndpoint( - url: '', - status: AuxCheckStatus.unknown, - ), + const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown), ]; } : null, diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index ac61019aaf..9fbc43a429 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -7,19 +7,11 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; class LocalNetworkPreference extends HookConsumerWidget { - const LocalNetworkPreference({ - super.key, - required this.enabled, - }); + const LocalNetworkPreference({super.key, required this.enabled}); final bool enabled; - Future _showEditDialog( - BuildContext context, - String title, - String hintText, - String initialValue, - ) { + Future _showEditDialog(BuildContext context, String title, String hintText, String initialValue) { final controller = TextEditingController(text: initialValue); return showDialog( @@ -29,23 +21,14 @@ class LocalNetworkPreference extends HookConsumerWidget { content: TextField( controller: controller, autofocus: true, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - ), + decoration: InputDecoration(border: const OutlineInputBorder(), hintText: hintText), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: Text( - 'cancel'.tr().toUpperCase(), - style: const TextStyle(color: Colors.red), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, controller.text), - child: Text('save'.tr().toUpperCase()), + child: Text('cancel'.tr().toUpperCase(), style: const TextStyle(color: Colors.red)), ), + TextButton(onPressed: () => Navigator.pop(context, controller.text), child: Text('save'.tr().toUpperCase())), ], ), ); @@ -56,23 +39,20 @@ class LocalNetworkPreference extends HookConsumerWidget { final wifiNameText = useState(""); final localEndpointText = useState(""); - useEffect( - () { - final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); - final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); + useEffect(() { + final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); + final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); - if (wifiName != null) { - wifiNameText.value = wifiName; - } + if (wifiName != null) { + wifiNameText.value = wifiName; + } - if (localEndpoint != null) { - localEndpointText.value = localEndpoint; - } + if (localEndpoint != null) { + localEndpointText.value = localEndpoint; + } - return null; - }, - [], - ); + return null; + }, []); saveWifiName(String wifiName) { wifiNameText.value = wifiName; @@ -85,12 +65,7 @@ class LocalNetworkPreference extends HookConsumerWidget { } handleEditWifiName() async { - final wifiName = await _showEditDialog( - context, - "wifi_name".tr(), - "your_wifi_name".tr(), - wifiNameText.value, - ); + final wifiName = await _showEditDialog(context, "wifi_name".tr(), "your_wifi_name".tr(), wifiNameText.value); if (wifiName != null) { await saveWifiName(wifiName); @@ -146,21 +121,14 @@ class LocalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.home_outlined, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.home_outlined, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -168,19 +136,11 @@ class LocalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "local_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("local_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), - Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + Divider(color: context.colorScheme.surfaceContainerHighest), ListTile( enabled: enabled, contentPadding: const EdgeInsets.only(left: 24, right: 8), @@ -223,9 +183,7 @@ class LocalNetworkPreference extends HookConsumerWidget { ), const SizedBox(height: 16), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 24.0), child: SizedBox( height: 48, child: OutlinedButton.icon( diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 24d62b2663..426ea5ac0f 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -77,15 +77,12 @@ class NetworkingSettings extends HookConsumerWidget { } } - useEffect( - () { - if (featureEnabled.value == true) { - checkWifiReadPermission(); - } - return null; - }, - [featureEnabled.value], - ); + useEffect(() { + if (featureEnabled.value == true) { + checkWifiReadPermission(); + } + return null; + }, [featureEnabled.value]); return ListView( padding: const EdgeInsets.only(bottom: 96), @@ -104,20 +101,12 @@ class NetworkingSettings extends HookConsumerWidget { elevation: 0, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(16)), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: ListTile( leading: currentEndpoint != null - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - ) - : const Icon( - Icons.circle_outlined, - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green) + : const Icon(Icons.circle_outlined), title: Text( currentEndpoint ?? "--", style: TextStyle( @@ -132,9 +121,7 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 10.0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest), ), SettingsSwitchListTile( enabled: true, @@ -144,35 +131,21 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 8, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "local_network".tr().toUpperCase(), - icon: Icons.home_outlined, - ), - ), - LocalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "local_network".tr().toUpperCase(), icon: Icons.home_outlined), ), + LocalNetworkPreference(enabled: featureEnabled.value), Padding( padding: const EdgeInsets.only(top: 32, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "external_network".tr().toUpperCase(), - icon: Icons.dns_outlined, - ), - ), - ExternalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "external_network".tr().toUpperCase(), icon: Icons.dns_outlined), ), + ExternalNetworkPreference(enabled: featureEnabled.value), ], ); } } class NetworkPreferenceTitle extends StatelessWidget { - const NetworkPreferenceTitle({ - super.key, - required this.icon, - required this.title, - }); + const NetworkPreferenceTitle({super.key, required this.icon, required this.title}); final IconData icon; final String title; @@ -181,10 +154,7 @@ class NetworkPreferenceTitle extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Icon( - icon, - color: context.colorScheme.onSurface.withAlpha(150), - ), + Icon(icon, color: context.colorScheme.onSurface.withAlpha(150)), const SizedBox(width: 8), Text( title, @@ -199,58 +169,37 @@ class NetworkPreferenceTitle extends StatelessWidget { } class NetworkStatusIcon extends StatelessWidget { - const NetworkStatusIcon({ - super.key, - required this.status, - this.enabled = true, - }) : super(); + const NetworkStatusIcon({super.key, required this.status, this.enabled = true}) : super(); final AuxCheckStatus status; final bool enabled; @override Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: _buildIcon(context), - ); + return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: _buildIcon(context)); } Widget _buildIcon(BuildContext context) => switch (status) { - AuxCheckStatus.loading => Padding( - padding: const EdgeInsets.only(left: 4.0), - child: SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: context.primaryColor, - strokeWidth: 2, - key: const ValueKey('loading'), - ), + AuxCheckStatus.loading => Padding( + padding: const EdgeInsets.only(left: 4.0), + child: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(color: context.primaryColor, strokeWidth: 2, key: const ValueKey('loading')), + ), + ), + AuxCheckStatus.valid => + enabled + ? const Icon(Icons.check_circle_rounded, color: Colors.green, key: ValueKey('success')) + : Icon( + Icons.check_circle_rounded, + color: context.colorScheme.onSurface.withAlpha(100), + key: const ValueKey('success'), ), - ), - AuxCheckStatus.valid => enabled - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - key: ValueKey('success'), - ) - : Icon( - Icons.check_circle_rounded, - color: context.colorScheme.onSurface.withAlpha(100), - key: const ValueKey('success'), - ), - AuxCheckStatus.error => enabled - ? const Icon( - Icons.error_rounded, - color: Colors.red, - key: ValueKey('error'), - ) - : const Icon( - Icons.error_rounded, - color: Colors.grey, - key: ValueKey('error'), - ), - _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), - }; + AuxCheckStatus.error => + enabled + ? const Icon(Icons.error_rounded, color: Colors.red, key: ValueKey('error')) + : const Icon(Icons.error_rounded, color: Colors.grey, key: ValueKey('error')), + _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), + }; } diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart index f4e520f4df..d9eab26bda 100644 --- a/mobile/lib/widgets/settings/notification_setting.dart +++ b/mobile/lib/widgets/settings/notification_setting.dart @@ -12,9 +12,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:permission_handler/permission_handler.dart'; class NotificationSetting extends HookConsumerWidget { - const NotificationSetting({ - super.key, - }); + const NotificationSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -39,14 +37,8 @@ class NotificationSetting extends HookConsumerWidget { builder: (ctx) => AlertDialog( content: const Text('notification_permission_dialog_content').tr(), actions: [ - TextButton( - child: const Text('cancel').tr(), - onPressed: () => ctx.pop(), - ), - TextButton( - onPressed: () => openAppNotificationSettings(ctx), - child: const Text('settings').tr(), - ), + TextButton(child: const Text('cancel').tr(), onPressed: () => ctx.pop()), + TextButton(onPressed: () => openAppNotificationSettings(ctx), child: const Text('settings').tr()), ], ), ); @@ -63,10 +55,10 @@ class NotificationSetting extends HookConsumerWidget { buttonText: 'notification_permission_list_tile_enable_button'.tr(), onButtonTap: () => ref.watch(notificationPermissionProvider.notifier).requestNotificationPermission().then((permission) { - if (permission == PermissionStatus.permanentlyDenied) { - showPermissionsDialog(); - } - }), + if (permission == PermissionStatus.permanentlyDenied) { + showPermissionsDialog(); + } + }), ), SettingsSwitchListTile( enabled: hasPermission, diff --git a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart index fbd94b68d6..49f57a5e94 100644 --- a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart @@ -8,9 +8,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class HapticSetting extends HookConsumerWidget { - const HapticSetting({ - super.key, - }); + const HapticSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart index 8a3684e093..144fbf9758 100644 --- a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/preference_settings/theme_setting import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; class PreferenceSetting extends StatelessWidget { - const PreferenceSetting({ - super.key, - }); + const PreferenceSetting({super.key}); @override Widget build(BuildContext context) { - const preferenceSettings = [ - ThemeSetting(), - HapticSetting(), - ]; + const preferenceSettings = [ThemeSetting(), HapticSetting()]; - return const SettingsSubPageScaffold( - settings: preferenceSettings, - showDivider: true, - ); + return const SettingsSubPageScaffold(settings: preferenceSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart index b4f70c5b9b..ddf2cc6215 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -12,9 +12,7 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class PrimaryColorSetting extends HookConsumerWidget { - const PrimaryColorSetting({ - super.key, - }); + const PrimaryColorSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -70,20 +68,14 @@ class PrimaryColorSetting extends HookConsumerWidget { Container( height: tileSize, width: tileSize, - decoration: BoxDecoration( - color: bottomColor, - borderRadius: const BorderRadius.all(Radius.circular(100)), - ), + decoration: BoxDecoration(color: bottomColor, borderRadius: const BorderRadius.all(Radius.circular(100))), ), Container( height: tileSize / 2, width: tileSize, decoration: BoxDecoration( color: topColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(100), - topRight: Radius.circular(100), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(100), topRight: Radius.circular(100)), ), ), if (showSelector) @@ -99,11 +91,7 @@ class PrimaryColorSetting extends HookConsumerWidget { ), child: const Padding( padding: EdgeInsets.all(3), - child: Icon( - Icons.check_rounded, - color: Colors.white, - size: 25, - ), + child: Icon(Icons.check_rounded, color: Colors.white, size: 25), ), ), ), @@ -118,10 +106,7 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Align( alignment: Alignment.center, - child: Text( - "theme_setting_primary_color_title".tr(), - style: context.textTheme.titleLarge, - ), + child: Text("theme_setting_primary_color_title".tr(), style: context.textTheme.titleLarge), ), if (DynamicTheme.isAvailable) Container( @@ -132,15 +117,10 @@ class PrimaryColorSetting extends HookConsumerWidget { dense: true, activeColor: context.primaryColor, tileColor: context.colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), title: Text( 'theme_setting_system_primary_color_title'.tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - height: 1.5, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5), ), value: systemPrimaryColorSetting.value, onChanged: onUseSystemColorChange, @@ -175,10 +155,7 @@ class PrimaryColorSetting extends HookConsumerWidget { context: context, isScrollControlled: true, builder: (BuildContext ctx) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), - child: bottomSheetContent(), - ); + return Padding(padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), child: bottomSheetContent()); }, ), contentPadding: const EdgeInsets.symmetric(horizontal: 20), @@ -190,9 +167,7 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Text( "theme_setting_primary_color_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), Text( "theme_setting_primary_color_subtitle".tr(), diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index 6d5a50e730..123f7c9921 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ThemeSetting extends HookConsumerWidget { - const ThemeSetting({ - super.key, - }); + const ThemeSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/settings_button_list_tile.dart b/mobile/lib/widgets/settings/settings_button_list_tile.dart index 602bbd75cf..0e8d75b22f 100644 --- a/mobile/lib/widgets/settings/settings_button_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_button_list_tile.dart @@ -31,12 +31,7 @@ class SettingsButtonListTile extends StatelessWidget { horizontalTitleGap: 20, isThreeLine: true, leading: Icon(icon, color: iconColor), - title: Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,9 +39,7 @@ class SettingsButtonListTile extends StatelessWidget { if (subtileText != null) Text( subtileText!, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), if (subtitle != null) subtitle!, const SizedBox(height: 6), diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart index 523add9690..36eff7bae1 100644 --- a/mobile/lib/widgets/settings/settings_card.dart +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -19,21 +19,15 @@ class SettingsCard extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Card( elevation: 0, clipBehavior: Clip.antiAlias, color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), margin: const EdgeInsets.symmetric(vertical: 4.0), child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), leading: Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), @@ -44,15 +38,9 @@ class SettingsCard extends StatelessWidget { ), title: Text( title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: context.textTheme.labelLarge, + style: context.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ), + subtitle: Text(subtitle, style: context.textTheme.labelLarge), onTap: () => context.pushRoute(settingRoute), ), ), diff --git a/mobile/lib/widgets/settings/settings_radio_list_tile.dart b/mobile/lib/widgets/settings/settings_radio_list_tile.dart index 3f3a6cbe69..95224e3f59 100644 --- a/mobile/lib/widgets/settings/settings_radio_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_radio_list_tile.dart @@ -13,12 +13,7 @@ class SettingsRadioListTile extends StatelessWidget { final T groupBy; final void Function(T?) onRadioChanged; - const SettingsRadioListTile({ - super.key, - required this.groups, - required this.groupBy, - required this.onRadioChanged, - }); + const SettingsRadioListTile({super.key, required this.groups, required this.groupBy, required this.onRadioChanged}); @override Widget build(BuildContext context) { @@ -29,12 +24,7 @@ class SettingsRadioListTile extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, activeColor: context.primaryColor, - title: Text( - g.title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(g.title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), value: g.value, groupValue: groupBy, onChanged: onRadioChanged, diff --git a/mobile/lib/widgets/settings/settings_slider_list_tile.dart b/mobile/lib/widgets/settings/settings_slider_list_tile.dart index 386a690864..500591badb 100644 --- a/mobile/lib/widgets/settings/settings_slider_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_slider_list_tile.dart @@ -28,12 +28,7 @@ class SettingsSliderListTile extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, - title: Text( - text, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(text, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Slider( value: valueNotifier.value.toDouble(), onChanged: (double v) => valueNotifier.value = v.toInt(), diff --git a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart index 96c4678ede..b4cb67239e 100644 --- a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart +++ b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart @@ -4,11 +4,7 @@ class SettingsSubPageScaffold extends StatelessWidget { final List settings; final bool showDivider; - const SettingsSubPageScaffold({ - super.key, - required this.settings, - this.showDivider = false, - }); + const SettingsSubPageScaffold({super.key, required this.settings, this.showDivider = false}); @override Widget build(BuildContext context) { @@ -18,11 +14,7 @@ class SettingsSubPageScaffold extends StatelessWidget { itemBuilder: (ctx, index) => settings[index], separatorBuilder: (context, index) => showDivider ? const Column( - children: [ - SizedBox(height: 5), - Divider(height: 10, indent: 15, endIndent: 15), - SizedBox(height: 15), - ], + children: [SizedBox(height: 5), Divider(height: 10, indent: 15, endIndent: 15), SizedBox(height: 15)], ) : const SizedBox(height: 10), ); diff --git a/mobile/lib/widgets/settings/settings_sub_title.dart b/mobile/lib/widgets/settings/settings_sub_title.dart index 9a3fb6947d..d98f1929b2 100644 --- a/mobile/lib/widgets/settings/settings_sub_title.dart +++ b/mobile/lib/widgets/settings/settings_sub_title.dart @@ -4,10 +4,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SettingsSubTitle extends StatelessWidget { final String title; - const SettingsSubTitle({ - super.key, - required this.title, - }); + const SettingsSubTitle({super.key, required this.title}); @override Widget build(BuildContext context) { @@ -15,10 +12,7 @@ class SettingsSubTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 20), child: Text( title, - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w700, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w700), ), ); } diff --git a/mobile/lib/widgets/settings/settings_switch_list_tile.dart b/mobile/lib/widgets/settings/settings_switch_list_tile.dart index 456acd83cd..d51e2eb2ca 100644 --- a/mobile/lib/widgets/settings/settings_switch_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_switch_list_tile.dart @@ -42,15 +42,11 @@ class SettingsSwitchListTile extends StatelessWidget { onChanged: onSwitchChanged, activeColor: enabled ? context.primaryColor : context.themeData.disabledColor, dense: true, - secondary: icon != null - ? Icon( - icon!, - color: valueNotifier.value ? context.primaryColor : null, - ) - : null, + secondary: icon != null ? Icon(icon!, color: valueNotifier.value ? context.primaryColor : null) : null, title: Text( title, - style: titleStyle ?? + style: + titleStyle ?? context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, color: enabled ? null : context.themeData.disabledColor, @@ -60,7 +56,8 @@ class SettingsSwitchListTile extends StatelessWidget { subtitle: subtitle != null ? Text( subtitle!, - style: subtitleStyle ?? + style: + subtitleStyle ?? context.textTheme.bodyMedium?.copyWith( color: enabled ? context.colorScheme.onSurfaceSecondary : context.themeData.disabledColor, ), diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index ae5b065294..dc31acf0a4 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -30,24 +30,15 @@ class _SslClientCertSettingsState extends State { contentPadding: const EdgeInsets.symmetric(horizontal: 20), horizontalTitleGap: 20, isThreeLine: true, - title: Text( - "client_cert_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text("client_cert_title".tr(), style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "client_cert_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - const SizedBox( - height: 6, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), + const SizedBox(height: 6), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -57,9 +48,7 @@ class _SslClientCertSettingsState extends State { onPressed: widget.isLoggedIn ? null : () => importCert(context), child: Text("client_cert_import".tr()), ), - const SizedBox( - width: 15, - ), + const SizedBox(width: 15), ElevatedButton( onPressed: widget.isLoggedIn || !isCertExist ? null : () async => await removeCert(context), child: Text("remove".tr()), @@ -76,39 +65,25 @@ class _SslClientCertSettingsState extends State { context: context, builder: (ctx) => AlertDialog( content: Text(message), - actions: [ - TextButton( - onPressed: () => ctx.pop(), - child: Text("client_cert_dialog_msg_confirm".tr()), - ), - ], + actions: [TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()))], ), ); } - Future storeCert( - BuildContext context, - Uint8List data, - String? password, - ) async { + Future storeCert(BuildContext context, Uint8List data, String? password) async { if (password != null && password.isEmpty) { password = null; } final cert = SSLClientCertStoreVal(data, password); // Test whether the certificate is valid - final isCertValid = HttpSSLCertOverride.setClientCert( - SecurityContext(withTrustedRoots: true), - cert, - ); + final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; } await cert.save(); HttpSSLOptions.apply(); - setState( - () => isCertExist = true, - ); + setState(() => isCertExist = true); showMessage(context, "client_cert_import_success_msg".tr()); } @@ -122,9 +97,7 @@ class _SslClientCertSettingsState extends State { controller: password, obscureText: true, obscuringCharacter: "*", - decoration: InputDecoration( - hintText: "client_cert_enter_password".tr(), - ), + decoration: InputDecoration(hintText: "client_cert_enter_password".tr()), ), actions: [ TextButton( @@ -139,10 +112,7 @@ class _SslClientCertSettingsState extends State { Future importCert(BuildContext ctx) async { FilePickerResult? res = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: [ - 'p12', - 'pfx', - ], + allowedExtensions: ['p12', 'pfx'], ); if (res != null) { File file = File(res.files.single.path!); @@ -154,9 +124,7 @@ class _SslClientCertSettingsState extends State { Future removeCert(BuildContext context) async { await SSLClientCertStoreVal.delete(); HttpSSLOptions.apply(); - setState( - () => isCertExist = false, - ); + setState(() => isCertExist = false); showMessage(context, "client_cert_remove_msg".tr()); } } diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index 82194d2c7c..0eced33ce3 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -33,10 +33,7 @@ class SharedLinkItem extends ConsumerWidget { var expiresText = "shared_link_expires_never".tr(); if (sharedLink.expiresAt != null) { if (isExpired()) { - return Text( - "expired", - style: TextStyle(color: Colors.red[300]), - ).tr(); + return Text("expired", style: TextStyle(color: Colors.red[300])).tr(); } final difference = sharedLink.expiresAt!.difference(DateTime.now()); debugPrint("Difference: $difference"); @@ -54,10 +51,7 @@ class SharedLinkItem extends ConsumerWidget { expiresText = "shared_link_expires_seconds".tr(namedArgs: {'count': difference.inSeconds.toString()}); } } - return Text( - expiresText, - style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600]), - ); + return Text(expiresText, style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600])); } @override @@ -68,9 +62,7 @@ class SharedLinkItem extends ConsumerWidget { final imageSize = math.min(context.width / 4, 100.0); void copyShareLinkToClipboard() { - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; @@ -85,16 +77,12 @@ class SharedLinkItem extends ConsumerWidget { return; } - Clipboard.setData( - ClipboardData(text: "${serverUrl}share/${sharedLink.key}"), - ).then((_) { + Clipboard.setData(ClipboardData(text: "${serverUrl}share/${sharedLink.key}")).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -120,14 +108,9 @@ class SharedLinkItem extends ConsumerWidget { return Container( height: imageSize * 1.2, width: imageSize, - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: isDarkMode ? Colors.grey[800] : Colors.grey[200]), child: Center( - child: Icon( - Icons.image_not_supported_outlined, - color: isDarkMode ? Colors.grey[100] : Colors.grey[700], - ), + child: Icon(Icons.image_not_supported_outlined, color: isDarkMode ? Colors.grey[100] : Colors.grey[700]), ), ); } @@ -160,9 +143,7 @@ class SharedLinkItem extends ConsumerWidget { color: isDarkMode ? Colors.black : Colors.white, ), ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(25)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25))), ), ); } @@ -228,10 +209,7 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.title, preferBelow: false, triggerMode: TooltipTriggerMode.tap, @@ -256,23 +234,14 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.description ?? "", preferBelow: false, triggerMode: TooltipTriggerMode.tap, - child: Text( - sharedLink.description ?? "", - overflow: TextOverflow.ellipsis, - ), + child: Text(sharedLink.description ?? "", overflow: TextOverflow.ellipsis), ), ), - Padding( - padding: const EdgeInsets.only(right: 15), - child: buildSharedLinkActions(), - ), + Padding(padding: const EdgeInsets.only(right: 15), child: buildSharedLinkActions()), ], ), buildBottomInfo(), @@ -286,24 +255,13 @@ class SharedLinkItem extends ConsumerWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 15), - child: buildThumbnail(), - ), + Padding(padding: const EdgeInsets.only(left: 15), child: buildThumbnail()), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 15), - child: buildSharedLinkDetails(), - ), + child: Padding(padding: const EdgeInsets.only(left: 15), child: buildSharedLinkDetails()), ), ], ), - const Padding( - padding: EdgeInsets.all(20), - child: Divider( - height: 0, - ), - ), + const Padding(padding: EdgeInsets.all(20), child: Divider(height: 0)), ], ); } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 20154649ea..42f37fbc85 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -2162,5 +2162,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0 <4.0.0" flutter: ">=3.32.8" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index cfec68689d..03519a0cf2 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: 'none' version: 1.136.0+3000 environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.8.0 <4.0.0' flutter: 3.32.8 isar_version: &isar_version 3.1.8 diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 262766662b..1534b2e914 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -21,10 +21,7 @@ void main() { late MockLocalAssetRepository mockAssetRepo; late MockStorageRepository mockStorageRepo; late MockNativeSyncApi mockNativeApi; - final sortBy = { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }; + final sortBy = {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}; setUp(() { mockAlbumRepo = MockLocalAlbumRepository(); @@ -47,9 +44,9 @@ void main() { group('HashService hashAssets', () { test('skips albums with no assets to hash', () async { - when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer( - (_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)], - ); + when( + () => mockAlbumRepo.getAll(sortBy: sortBy), + ).thenAnswer((_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)]); when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)).thenAnswer((_) async => []); await sut.hashAssets(); @@ -84,9 +81,7 @@ void main() { when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer( - (_) async => [hash], - ); + when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [hash]); await sut.hashAssets(); diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index ad35a018c8..87b32b8298 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -43,10 +43,7 @@ void main() { when(() => mockLogRepo.insert(any())).thenAnswer((_) async => true); when(() => mockLogRepo.insertAll(any())).thenAnswer((_) async => true); - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo); }); tearDown(() async { @@ -72,9 +69,7 @@ void main() { }); test('Updates the log level in store', () { - final index = verify( - () => mockStoreRepo.insert(StoreKey.logLevel, captureAny()), - ).captured.firstOrNull; + final index = verify(() => mockStoreRepo.insert(StoreKey.logLevel, captureAny())).captured.firstOrNull; expect(index, LogLevel.shout.index); }); @@ -86,11 +81,7 @@ void main() { group("Log Service Buffer:", () { test('Buffers logs until timer elapses', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -104,11 +95,7 @@ void main() { test('Batch inserts all logs on timer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -125,11 +112,7 @@ void main() { test('Does not buffer when off', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: false, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: false); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -159,11 +142,7 @@ void main() { test('Combines result from both DB + Buffer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kWarnLog.logger!); logger.warning(_kWarnLog.message); diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index c436a05454..d23913991c 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -73,10 +73,7 @@ void main() { }); test('Throws StoreKeyNotFoundException for nonexistent keys', () { - expect( - () => sut.get(StoreKey.currentUser), - throwsA(isA()), - ); + expect(() => sut.get(StoreKey.currentUser), throwsA(isA())); }); test('Returns the stored value for the given key or the defaultValue', () { @@ -91,17 +88,13 @@ void main() { test('Skip insert when value is not modified', () async { await sut.put(StoreKey.accessToken, _kAccessToken); - verifyNever( - () => mockStoreRepo.insert(StoreKey.accessToken, any()), - ); + verifyNever(() => mockStoreRepo.insert(StoreKey.accessToken, any())); }); test('Insert value when modified', () async { final newAccessToken = _kAccessToken.toUpperCase(); await sut.put(StoreKey.accessToken, newAccessToken); - verify( - () => mockStoreRepo.insert(StoreKey.accessToken, newAccessToken), - ).called(1); + verify(() => mockStoreRepo.insert(StoreKey.accessToken, newAccessToken)).called(1); expect(sut.tryGet(StoreKey.accessToken), newAccessToken); }); }); @@ -120,12 +113,7 @@ void main() { test('Watches a specific key for changes', () async { final stream = sut.watch(StoreKey.accessToken); - final events = [ - _kAccessToken, - _kAccessToken.toUpperCase(), - null, - _kAccessToken.toLowerCase(), - ]; + final events = [_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; expectLater(stream, emitsInOrder(events)); diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index 49ac4467d0..46e585faa0 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -54,40 +54,25 @@ void main() { when(() => mockSyncStreamRepo.deletePartnerV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsExifV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsExifV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsExifV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteMemoriesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())).thenAnswer(successHandler); @@ -96,10 +81,7 @@ void main() { when(() => mockSyncStreamRepo.updateAssetFacesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())).thenAnswer(successHandler); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - ); + sut = SyncStreamService(syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo); }); Future simulateEvents(List events) async { @@ -108,41 +90,35 @@ void main() { } group("SyncStreamService - _handleEvents", () { - test( - "processes events and acks successfully when handlers succeed", - () async { - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.userV1User, - SyncStreamStub.partnerDeleteV1, - SyncStreamStub.partnerV1, - ]; - - await simulateEvents(events); - - verifyInOrder([ - () => mockSyncStreamRepo.deleteUsersV1(any()), - () => mockSyncApiRepo.ack(["2"]), - () => mockSyncStreamRepo.updateUsersV1(any()), - () => mockSyncApiRepo.ack(["5"]), - () => mockSyncStreamRepo.deletePartnerV1(any()), - () => mockSyncApiRepo.ack(["4"]), - () => mockSyncStreamRepo.updatePartnerV1(any()), - () => mockSyncApiRepo.ack(["3"]), - ]); - verifyNever(() => mockAbortCallbackWrapper()); - }, - ); - - test("processes final batch correctly", () async { + test("processes events and acks successfully when handlers succeed", () async { final events = [ SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, + SyncStreamStub.userV1User, + SyncStreamStub.partnerDeleteV1, + SyncStreamStub.partnerV1, ]; await simulateEvents(events); + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deletePartnerV1(any()), + () => mockSyncApiRepo.ack(["4"]), + () => mockSyncStreamRepo.updatePartnerV1(any()), + () => mockSyncApiRepo.ack(["3"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("processes final batch correctly", () async { + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin]; + + await simulateEvents(events); + verifyInOrder([ () => mockSyncStreamRepo.deleteUsersV1(any()), () => mockSyncApiRepo.ack(["2"]), @@ -174,11 +150,7 @@ void main() { ); await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { when(() => cancellationChecker()).thenReturn(true); @@ -195,50 +167,43 @@ void main() { verify(() => mockSyncApiRepo.ack(["2"])).called(1); }); - test( - "aborts and stops processing if cancelled before processing batch", - () async { - final cancellationChecker = _MockCancellationWrapper(); - when(() => cancellationChecker()).thenReturn(false); + test("aborts and stops processing if cancelled before processing batch", () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); - final processingCompleter = Completer(); - bool handler1Started = false; - when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { - handler1Started = true; - return processingCompleter.future; - }); + final processingCompleter = Completer(); + bool handler1Started = false; + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { + handler1Started = true; + return processingCompleter.future; + }); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - cancelChecker: cancellationChecker.call, - ); + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); - await sut.sync(); + await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; - final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call); - await pumpEventQueue(); + final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call); + await pumpEventQueue(); - expect(handler1Started, isTrue); + expect(handler1Started, isTrue); - // Signal cancellation while handler 1 is waiting - when(() => cancellationChecker()).thenReturn(true); - await pumpEventQueue(); + // Signal cancellation while handler 1 is waiting + when(() => cancellationChecker()).thenReturn(true); + await pumpEventQueue(); - processingCompleter.complete(); - await processingFuture; + processingCompleter.complete(); + await processingFuture; - verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); - verify(() => mockSyncApiRepo.ack(["2"])).called(1); - }, - ); + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }); test("processes memory sync events successfully", () async { final events = [ @@ -289,15 +254,9 @@ void main() { test("handles memory sync failure gracefully", () async { when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenThrow(Exception("Memory sync failed")); - final events = [ - SyncStreamStub.memoryV1, - SyncStreamStub.userV1Admin, - ]; + final events = [SyncStreamStub.memoryV1, SyncStreamStub.userV1Admin]; - expect( - () async => await simulateEvents(events), - throwsA(isA()), - ); + expect(() async => await simulateEvents(events), throwsA(isA())); }); test("processes memory asset events with correct data types", () async { diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index b26c243430..b3d967154c 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -89,9 +89,7 @@ void main() { when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null); final result = await sut.refreshMyUser(); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, UserStub.admin), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)); verifyNever(() => mockUserRepo.update(UserStub.admin)); expect(result, isNull); }); @@ -103,10 +101,7 @@ void main() { final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenAnswer((_) async => profileImagePath); when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true); when(() => mockUserRepo.update(updatedUser)).thenAnswer((_) async => UserStub.admin); @@ -123,16 +118,11 @@ void main() { final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenThrow(Exception('Failed to create profile image')); final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, updatedUser), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, updatedUser)); verifyNever(() => mockUserRepo.update(updatedUser)); expect(result, isNull); }); diff --git a/mobile/test/drift/main/generated/schema_v1.dart b/mobile/test/drift/main/generated/schema_v1.dart index 75f3bdee4c..ca9e6ca1b0 100644 --- a/mobile/test/drift/main/generated/schema_v1.dart +++ b/mobile/test/drift/main/generated/schema_v1.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,26 +1667,57 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1307,12 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1333,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1350,7 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1372,22 +1812,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1404,7 +1850,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1435,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1454,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1503,19 +1951,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1529,9 +1994,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1546,11 +2020,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1560,7 +2039,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1578,11 +2060,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1612,7 +2098,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1625,9 +2112,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1640,7 +2127,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1674,24 +2165,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1705,9 +2215,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1722,11 +2241,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1736,7 +2260,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1754,16 +2281,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1801,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1815,7 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1849,32 +2390,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1886,13 +2466,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1907,20 +2504,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1935,7 +2534,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1959,28 +2561,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1999,7 +2605,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2034,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2055,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2110,19 +2724,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2133,11 +2760,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2152,10 +2788,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2164,7 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2180,11 +2823,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2205,10 +2851,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2218,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2230,7 +2879,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2259,83 +2911,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2347,29 +3111,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2384,7 +3213,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2407,29 +3237,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2500,14 +3331,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2556,77 +3392,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2661,29 +3513,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2833,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2984,43 +3837,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3032,17 +3942,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3057,7 +3992,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3067,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3094,7 +4031,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3124,37 +4064,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3176,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3193,7 +4150,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3224,10 +4182,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3252,16 +4210,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3325,19 +4284,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3348,11 +4320,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3367,10 +4348,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3379,7 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3395,11 +4383,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3420,10 +4411,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3433,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3445,7 +4439,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3474,21 +4471,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3499,12 +4514,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3519,11 +4546,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3533,7 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3551,12 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3585,7 +4626,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3598,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3613,7 +4655,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3647,47 +4693,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3699,18 +4818,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3725,7 +4880,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3738,19 +4894,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3777,7 +4934,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3813,33 +4973,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3877,8 +5037,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3937,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3972,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4063,19 +5236,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4089,8 +5275,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4105,7 +5297,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4117,7 +5310,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4133,7 +5329,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4158,10 +5355,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4171,8 +5371,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4183,7 +5383,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4212,46 +5415,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4263,17 +5534,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4288,7 +5592,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4300,18 +5605,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4335,7 +5641,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4369,31 +5678,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4401,9 +5710,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4430,7 +5745,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4485,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4519,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4610,48 +5937,59 @@ class DatabaseAtV1 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 1; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v2.dart b/mobile/test/drift/main/generated/schema_v2.dart index 3d705e0454..903d13b9a2 100644 --- a/mobile/test/drift/main/generated/schema_v2.dart +++ b/mobile/test/drift/main/generated/schema_v2.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,26 +1667,57 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1307,12 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1333,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1350,7 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1372,22 +1812,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1404,7 +1850,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1435,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1454,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1503,19 +1951,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1529,9 +1994,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1546,11 +2020,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1560,7 +2039,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1578,11 +2060,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1612,7 +2098,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1625,9 +2112,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1640,7 +2127,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1674,24 +2165,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1705,9 +2215,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1722,11 +2241,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1736,7 +2260,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1754,16 +2281,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1801,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1815,7 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1849,32 +2390,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1886,13 +2466,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1907,20 +2504,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1935,7 +2534,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1959,28 +2561,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1999,7 +2605,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2034,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2055,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2110,19 +2724,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2133,11 +2760,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2152,10 +2788,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2164,7 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2180,11 +2823,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2205,10 +2851,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2218,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2230,7 +2879,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2259,83 +2911,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2347,29 +3111,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2384,7 +3213,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2407,29 +3237,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2500,14 +3331,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2556,77 +3392,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2661,29 +3513,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2833,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2984,43 +3837,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3032,17 +3942,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3057,7 +3992,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3067,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3094,7 +4031,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3124,37 +4064,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3176,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3193,7 +4150,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3224,10 +4182,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3252,16 +4210,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3325,19 +4284,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3348,11 +4320,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3367,10 +4348,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3379,7 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3395,11 +4383,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3420,10 +4411,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3433,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3445,7 +4439,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3474,21 +4471,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3499,12 +4514,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3519,11 +4546,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3533,7 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3551,12 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3585,7 +4626,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3598,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3613,7 +4655,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3647,47 +4693,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3699,18 +4818,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3725,7 +4880,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3738,19 +4894,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3777,7 +4934,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3813,33 +4973,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3877,8 +5037,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3937,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3972,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4063,19 +5236,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4089,8 +5275,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4105,7 +5297,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4117,7 +5310,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4133,7 +5329,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4158,10 +5355,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4171,8 +5371,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4183,7 +5383,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4212,46 +5415,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4263,17 +5534,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4288,7 +5592,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4300,18 +5605,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4335,7 +5641,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4369,31 +5678,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4401,9 +5710,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4430,7 +5745,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4485,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4519,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4610,48 +5937,59 @@ class DatabaseAtV2 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 2; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v3.dart b/mobile/test/drift/main/generated/schema_v3.dart index 711cf85253..e4382a9fb9 100644 --- a/mobile/test/drift/main/generated/schema_v3.dart +++ b/mobile/test/drift/main/generated/schema_v3.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,24 +1667,54 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1305,12 +1726,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1331,12 +1766,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1348,7 +1784,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1370,22 +1809,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1402,7 +1847,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1433,9 +1879,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1452,12 +1898,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1501,19 +1948,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1527,9 +1991,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1544,11 +2017,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1558,7 +2036,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1576,11 +2057,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1610,7 +2095,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1623,9 +2109,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1638,7 +2124,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1672,24 +2162,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1703,9 +2212,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1720,11 +2238,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,7 +2257,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1752,16 +2278,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1799,8 +2335,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1813,7 +2349,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1847,32 +2387,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1884,13 +2463,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1905,20 +2501,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1933,7 +2531,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1957,28 +2558,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1997,7 +2602,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2032,9 +2644,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2053,13 +2665,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2108,19 +2721,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2131,11 +2757,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2150,10 +2785,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2162,7 +2801,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2178,11 +2820,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2203,10 +2848,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2216,8 +2864,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2228,7 +2876,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2257,83 +2908,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2345,29 +3108,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2382,7 +3210,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2405,29 +3234,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2498,14 +3328,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2554,77 +3389,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2659,29 +3510,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2831,29 +3682,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2982,43 +3834,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3030,17 +3939,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3055,7 +3989,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3065,16 +4000,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3092,7 +4028,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3122,37 +4061,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3174,8 +4121,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3191,7 +4147,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3222,10 +4179,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3250,16 +4207,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3323,19 +4281,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3346,11 +4317,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3365,10 +4345,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3377,7 +4361,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3393,11 +4380,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3418,10 +4408,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3431,8 +4424,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3443,7 +4436,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3472,21 +4468,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3497,12 +4511,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3517,11 +4543,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3531,7 +4562,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3549,12 +4583,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3583,7 +4623,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3596,9 +4637,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3611,7 +4652,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3645,47 +4690,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3697,18 +4815,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3723,7 +4877,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3736,19 +4891,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3775,7 +4931,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3811,33 +4970,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3875,8 +5034,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3935,11 +5106,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3970,19 +5141,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4061,19 +5233,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4087,8 +5272,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4103,7 +5294,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4115,7 +5307,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4131,7 +5326,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4156,10 +5352,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4169,8 +5368,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4181,7 +5380,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4210,46 +5412,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4261,17 +5531,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4286,7 +5589,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4298,18 +5602,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4333,7 +5638,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4367,31 +5675,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4399,9 +5707,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4428,7 +5742,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4483,12 +5808,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4517,18 +5842,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4608,48 +5934,59 @@ class DatabaseAtV3 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 3; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index f54e5e9644..9eabea714f 100644 --- a/mobile/test/drift/main/generated/schema_v4.dart +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -887,24 +1154,54 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -916,12 +1213,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -942,12 +1253,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -959,7 +1271,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -981,22 +1296,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1013,7 +1334,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1044,9 +1366,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1063,12 +1385,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1112,43 +1435,110 @@ class StackEntityCompanion extends UpdateCompanion { } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1160,18 +1550,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1186,7 +1608,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -1198,18 +1621,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1235,7 +1659,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1269,31 +1696,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1302,11 +1731,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1385,9 +1831,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1416,18 +1862,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1501,32 +1948,71 @@ class LocalAssetEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1538,13 +2024,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1559,20 +2062,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1587,7 +2092,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1611,28 +2119,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1651,7 +2163,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1686,9 +2205,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -1707,13 +2226,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -1762,19 +2282,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -1785,11 +2318,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -1804,10 +2346,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1816,7 +2362,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -1832,11 +2381,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -1857,10 +2409,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -1870,8 +2425,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -1882,7 +2437,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -1911,19 +2469,36 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1937,9 +2512,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1954,11 +2538,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1968,7 +2557,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1986,11 +2578,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -2020,7 +2616,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -2033,9 +2630,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -2048,7 +2645,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -2082,24 +2683,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -2113,9 +2733,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -2130,11 +2759,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2144,7 +2778,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -2162,16 +2799,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2209,8 +2856,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2223,7 +2870,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2257,83 +2908,195 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class RemoteExifEntity extends Table with TableInfo { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2345,29 +3108,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2382,7 +3210,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2405,29 +3234,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2498,14 +3328,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2554,77 +3389,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2659,29 +3510,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2831,29 +3682,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2982,43 +3834,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3030,17 +3939,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3055,7 +3989,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3065,16 +4000,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3092,7 +4028,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3122,37 +4061,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3174,8 +4121,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3191,7 +4147,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3222,10 +4179,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3250,16 +4207,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3323,19 +4281,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3346,11 +4317,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3365,10 +4345,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3377,7 +4361,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3393,11 +4380,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3418,10 +4408,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3431,8 +4424,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3443,7 +4436,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3472,21 +4468,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3497,12 +4511,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3517,11 +4543,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3531,7 +4562,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3549,12 +4583,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3583,7 +4623,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3596,9 +4637,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3611,7 +4652,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3645,47 +4690,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3697,18 +4815,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3723,7 +4877,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3736,19 +4891,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3775,7 +4931,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3811,33 +4970,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3875,8 +5034,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3935,11 +5106,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3970,19 +5141,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4061,19 +5233,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4087,8 +5272,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4103,7 +5294,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4115,7 +5307,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4131,7 +5326,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4156,10 +5352,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4169,8 +5368,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4181,7 +5380,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4210,44 +5412,106 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4259,16 +5523,46 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4283,7 +5577,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4294,17 +5589,18 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4327,7 +5623,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4359,29 +5658,29 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4389,8 +5688,12 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4415,8 +5718,18 @@ class PersonEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, ownerId, name, faceAssetId, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4467,11 +5780,11 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4498,17 +5811,18 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4577,48 +5891,101 @@ class PersonEntityCompanion extends UpdateCompanion { } } -class AssetFaceEntity extends Table with TableInfo { +class AssetFaceEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; AssetFaceEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn personId = GeneratedColumn('person_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES person_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn imageWidth = - GeneratedColumn('image_width', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn imageHeight = - GeneratedColumn('image_height', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxX1 = - GeneratedColumn('bounding_box_x1', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxY1 = - GeneratedColumn('bounding_box_y1', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxX2 = - GeneratedColumn('bounding_box_x2', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxY2 = - GeneratedColumn('bounding_box_y2', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn sourceType = - GeneratedColumn('source_type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType - ]; + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4630,16 +5997,46 @@ class AssetFaceEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return AssetFaceEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - personId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}person_id']), - imageWidth: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}image_width'])!, - imageHeight: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}image_height'])!, - boundingBoxX1: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, - boundingBoxY1: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, - boundingBoxX2: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, - boundingBoxY2: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, - sourceType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}source_type'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, ); } @@ -4654,7 +6051,8 @@ class AssetFaceEntity extends Table with TableInfo true; } -class AssetFaceEntityData extends DataClass implements Insertable { +class AssetFaceEntityData extends DataClass + implements Insertable { final String id; final String assetId; final String? personId; @@ -4665,17 +6063,18 @@ class AssetFaceEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -4694,7 +6093,10 @@ class AssetFaceEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return AssetFaceEntityData( id: serializer.fromJson(json['id']), @@ -4726,41 +6128,55 @@ class AssetFaceEntityData extends DataClass implements Insertable personId = const Value.absent(), - int? imageWidth, - int? imageHeight, - int? boundingBoxX1, - int? boundingBoxY1, - int? boundingBoxX2, - int? boundingBoxY2, - String? sourceType}) => - AssetFaceEntityData( - id: id ?? this.id, - assetId: assetId ?? this.assetId, - personId: personId.present ? personId.value : this.personId, - imageWidth: imageWidth ?? this.imageWidth, - imageHeight: imageHeight ?? this.imageHeight, - boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, - boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, - boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, - boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, - sourceType: sourceType ?? this.sourceType, - ); + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { return AssetFaceEntityData( id: data.id.present ? data.id.value : this.id, assetId: data.assetId.present ? data.assetId.value : this.assetId, personId: data.personId.present ? data.personId.value : this.personId, - imageWidth: data.imageWidth.present ? data.imageWidth.value : this.imageWidth, - imageHeight: data.imageHeight.present ? data.imageHeight.value : this.imageHeight, - boundingBoxX1: data.boundingBoxX1.present ? data.boundingBoxX1.value : this.boundingBoxX1, - boundingBoxY1: data.boundingBoxY1.present ? data.boundingBoxY1.value : this.boundingBoxY1, - boundingBoxX2: data.boundingBoxX2.present ? data.boundingBoxX2.value : this.boundingBoxX2, - boundingBoxY2: data.boundingBoxY2.present ? data.boundingBoxY2.value : this.boundingBoxY2, - sourceType: data.sourceType.present ? data.sourceType.value : this.sourceType, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, ); } @@ -4782,8 +6198,18 @@ class AssetFaceEntityData extends DataClass implements Insertable Object.hash(id, assetId, personId, imageWidth, imageHeight, boundingBoxX1, boundingBoxY1, - boundingBoxX2, boundingBoxY2, sourceType); + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4834,15 +6260,15 @@ class AssetFaceEntityCompanion extends UpdateCompanion { required int boundingBoxX2, required int boundingBoxY2, required String sourceType, - }) : id = Value(id), - assetId = Value(assetId), - imageWidth = Value(imageWidth), - imageHeight = Value(imageHeight), - boundingBoxX1 = Value(boundingBoxX1), - boundingBoxY1 = Value(boundingBoxY1), - boundingBoxX2 = Value(boundingBoxX2), - boundingBoxY2 = Value(boundingBoxY2), - sourceType = Value(sourceType); + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); static Insertable custom({ Expression? id, Expression? assetId, @@ -4869,17 +6295,18 @@ class AssetFaceEntityCompanion extends UpdateCompanion { }); } - AssetFaceEntityCompanion copyWith( - {Value? id, - Value? assetId, - Value? personId, - Value? imageWidth, - Value? imageHeight, - Value? boundingBoxX1, - Value? boundingBoxY1, - Value? boundingBoxX2, - Value? boundingBoxY2, - Value? sourceType}) { + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { return AssetFaceEntityCompanion( id: id ?? this.id, assetId: assetId ?? this.assetId, @@ -4955,49 +6382,60 @@ class DatabaseAtV4 extends GeneratedDatabase { late final StackEntity stackEntity = StackEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumEntity, - localAlbumAssetEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity, - assetFaceEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; @override int get schemaVersion => 4; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index 1e79f62faf..a22a4b72ab 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -42,20 +42,21 @@ final class AlbumStub { endDate: DateTime(2023), )..assets.addAll([AssetStub.image1]); - static final twoAsset = Album( - name: "album-with-two-assets", - localId: "album-with-two-assets-local", - remoteId: "album-with-two-assets-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..assets.addAll([AssetStub.image1, AssetStub.image2]) - ..activityEnabled = true - ..owner.value = User.fromDto(UserStub.admin); + static final twoAsset = + Album( + name: "album-with-two-assets", + localId: "album-with-two-assets-local", + remoteId: "album-with-two-assets-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..assets.addAll([AssetStub.image1, AssetStub.image2]) + ..activityEnabled = true + ..owner.value = User.fromDto(UserStub.admin); static final create2020end2020Album = Album( name: "create2020update2020Album", diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index 6c47b9172e..a89be5ad24 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -4,24 +4,12 @@ import 'package:openapi/api.dart'; abstract final class SyncStreamStub { static final userV1Admin = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1( - deletedAt: DateTime(2020), - email: "admin@admin", - id: "1", - name: "Admin", - avatarColor: null, - ), + data: SyncUserV1(deletedAt: DateTime(2020), email: "admin@admin", id: "1", name: "Admin", avatarColor: null), ack: "1", ); static final userV1User = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1( - deletedAt: DateTime(2021), - email: "user@user", - id: "5", - name: "User", - avatarColor: null, - ), + data: SyncUserV1(deletedAt: DateTime(2021), email: "user@user", id: "5", name: "User", avatarColor: null), ack: "5", ); static final userDeleteV1 = SyncEvent( @@ -32,11 +20,7 @@ abstract final class SyncStreamStub { static final partnerV1 = SyncEvent( type: SyncEntityType.partnerV1, - data: SyncPartnerV1( - inTimeline: true, - sharedById: "1", - sharedWithId: "2", - ), + data: SyncPartnerV1(inTimeline: true, sharedById: "1", sharedWithId: "2"), ack: "3", ); static final partnerDeleteV1 = SyncEvent( @@ -72,19 +56,13 @@ abstract final class SyncStreamStub { static final memoryToAssetV1 = SyncEvent( type: SyncEntityType.memoryToAssetV1, - data: SyncMemoryAssetV1( - assetId: "asset-1", - memoryId: "memory-1", - ), + data: SyncMemoryAssetV1(assetId: "asset-1", memoryId: "memory-1"), ack: "7", ); static final memoryToAssetDeleteV1 = SyncEvent( type: SyncEntityType.memoryToAssetDeleteV1, - data: SyncMemoryAssetDeleteV1( - assetId: "asset-2", - memoryId: "memory-1", - ), + data: SyncMemoryAssetDeleteV1(assetId: "asset-2", memoryId: "memory-1"), ack: "8", ); } diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index bb4292be07..fae0e09171 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -12,48 +12,21 @@ void main() { late MediumFactory mediumFactory; setUp(() { - db = Drift( - DatabaseConnection( - NativeDatabase.memory(), - closeStreamsSynchronously: true, - ), - ); + db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); mediumFactory = MediumFactory(db); }); group('getAll', () { test('sorts albums by backupSelection & isIosSharedAlbum', () async { final localAlbumRepo = mediumFactory.getRepository(); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '1', backupSelection: BackupSelection.none)); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '2', backupSelection: BackupSelection.excluded)); await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '1', - backupSelection: BackupSelection.none, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '2', - backupSelection: BackupSelection.excluded, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '3', - backupSelection: BackupSelection.selected, - isIosSharedAlbum: true, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '4', - backupSelection: BackupSelection.selected, - ), + mediumFactory.localAlbum(id: '3', backupSelection: BackupSelection.selected, isIosSharedAlbum: true), ); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '4', backupSelection: BackupSelection.selected)); final albums = await localAlbumRepo.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); expect(albums.length, 4); expect(albums[0].id, '4'); // selected diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index 6d75fbc765..84d18ad955 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -24,16 +24,8 @@ Future _addStrStoreValue(Isar db, StoreKey key, String? value) async { Future _populateStore(Isar db) async { await db.writeTxn(() async { - await _addIntStoreValue( - db, - StoreKey.colorfulInterface, - _kTestColorfulInterface ? 1 : 0, - ); - await _addIntStoreValue( - db, - StoreKey.backupFailedSince, - _kTestBackupFailed.millisecondsSinceEpoch, - ); + await _addIntStoreValue(db, StoreKey.colorfulInterface, _kTestColorfulInterface ? 1 : 0); + await _addIntStoreValue(db, StoreKey.backupFailedSince, _kTestBackupFailed.millisecondsSinceEpoch); await _addStrStoreValue(db, StoreKey.accessToken, _kTestAccessToken); await _addIntStoreValue(db, StoreKey.version, _kTestVersion); }); @@ -143,21 +135,10 @@ void main() { stream, emitsInAnyOrder([ emits(const StoreDto(StoreKey.version, _kTestVersion)), - emits( - StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - ), - emits( - const StoreDto(StoreKey.accessToken, _kTestAccessToken), - ), - emits( - const StoreDto( - StoreKey.colorfulInterface, - _kTestColorfulInterface, - ), - ), - emits( - const StoreDto(StoreKey.version, _kTestVersion + 10), - ), + emits(StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed)), + emits(const StoreDto(StoreKey.accessToken, _kTestAccessToken)), + emits(const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface)), + emits(const StoreDto(StoreKey.version, _kTestVersion + 10)), ]), ); await sut.update(StoreKey.version, _kTestVersion + 10); diff --git a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart index 3348cc7c04..d456b06f7c 100644 --- a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart +++ b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart @@ -63,14 +63,8 @@ void main() { } }); - Future streamChanges( - Function(List, Function() abort) onDataCallback, - ) { - return sut.streamChanges( - onDataCallback, - batchSize: testBatchSize, - httpClient: mockHttpClient, - ); + Future streamChanges(Function(List, Function() abort) onDataCallback) { + return sut.streamChanges(onDataCallback, batchSize: testBatchSize, httpClient: mockHttpClient); } test('streamChanges stops processing stream when abort is called', () async { @@ -96,11 +90,7 @@ void main() { for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -108,11 +98,7 @@ void main() { for (int i = testBatchSize; i < testBatchSize * 2; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -126,113 +112,91 @@ void main() { verify(() => mockHttpClient.close()).called(1); }); - test( - 'streamChanges does not process remaining lines in finally block if aborted', - () async { - int onDataCallCount = 0; - bool abortWasCalledInCallback = false; + test('streamChanges does not process remaining lines in finally block if aborted', () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; - onDataCallback(List events, Function() abort) { - onDataCallCount++; - if (onDataCallCount == 1) { - abort(); - abortWasCalledInCallback = true; - } else { - fail("onData called more than once after abort was invoked"); - } + onDataCallback(List events, Function() abort) { + onDataCallCount++; + if (onDataCallCount == 1) { + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // emit a single event to skip batching and trigger finally + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // emit a single event to skip batching and trigger finally + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 1); - expect(abortWasCalledInCallback, isTrue); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); - test( - 'streamChanges processes remaining lines in finally block if not aborted', - () async { - int onDataCallCount = 0; - List receivedEventsBatch1 = []; - List receivedEventsBatch2 = []; + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + verify(() => mockHttpClient.close()).called(1); + }); - onDataCallback(List events, Function() _) { - onDataCallCount++; - if (onDataCallCount == 1) { - receivedEventsBatch1 = events; - } else if (onDataCallCount == 2) { - receivedEventsBatch2 = events; - } else { - fail("onData called more than expected"); - } + test('streamChanges processes remaining lines in finally block if not aborted', () async { + int onDataCallCount = 0; + List receivedEventsBatch1 = []; + List receivedEventsBatch2 = []; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + } else if (onDataCallCount == 2) { + receivedEventsBatch2 = events; + } else { + fail("onData called more than expected"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - // Batch 1 - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // Partial Batch 2 + // Batch 1 + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // Partial Batch 2 + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 2); - expect(receivedEventsBatch1.length, testBatchSize); - expect(receivedEventsBatch2.length, 1); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 2); + expect(receivedEventsBatch1.length, testBatchSize); + expect(receivedEventsBatch2.length, 1); + verify(() => mockHttpClient.close()).called(1); + }); test('streamChanges handles stream error gracefully', () async { final streamError = Exception("Network Error"); @@ -248,11 +212,7 @@ void main() { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user1").toJson(), - 'ack1', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user1").toJson(), 'ack1'), ), ); diff --git a/mobile/test/mock_http_override.dart b/mobile/test/mock_http_override.dart index ae19d932c0..877d6f8726 100644 --- a/mobile/test/mock_http_override.dart +++ b/mobile/test/mock_http_override.dart @@ -40,12 +40,9 @@ class MockHttpOverrides extends HttpOverrides { final cancelOnError = invocation.namedArguments[#cancelOnError] as bool; - return Stream>.fromIterable([kTransparentImage.toList()]).listen( - onData, - onDone: onDone, - onError: onError, - cancelOnError: cancelOnError, - ); + return Stream>.fromIterable([ + kTransparentImage.toList(), + ]).listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); return client; diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index c3279e9b58..05eac98111 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -49,19 +49,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.user2, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.user2), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -85,10 +74,7 @@ void main() { mockCurrentAssetProvider = MockCurrentAssetProvider(AssetStub.image1); activityMock = MockAlbumActivity(_activities); overrides = [ - albumActivityProvider( - AlbumStub.twoAsset.remoteId!, - AssetStub.image1.remoteId!, - ).overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!, AssetStub.image1.remoteId!).overrideWith(() => activityMock), currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), currentAssetProvider.overrideWith(() => mockCurrentAssetProvider), ]; @@ -108,147 +94,82 @@ void main() { }); group("App bar", () { - testWidgets( - "No title when currentAsset != null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); + testWidgets("No title when currentAsset != null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNull); - }, - ); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNull); + }); - testWidgets( - "Album name as title when currentAsset == null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Album name as title when currentAsset == null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - mockCurrentAssetProvider.state = null; - await tester.pumpAndSettle(); + mockCurrentAssetProvider.state = null; + await tester.pumpAndSettle(); - expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNotNull); - }, - ); + expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNotNull); + }); }); group("Body", () { - testWidgets( - "Contains a stack with Activity List and Activity Input", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Contains a stack with Activity List and Activity Input", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ActivityTextField), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ActivityTextField)), findsOneWidget); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ), - findsOneWidget, - ); - }, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ListView)), findsOneWidget); + }); - testWidgets( - "List Contains all dismissible activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("List Contains all dismissible activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final listFinder = find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ); - final listChildren = find.descendant( - of: listFinder, - matching: find.byType(DismissibleActivity), - ); - expect(listChildren, findsNWidgets(_activities.length)); - }, - ); + final listFinder = find.descendant(of: find.byType(Stack), matching: find.byType(ListView)); + final listChildren = find.descendant(of: listFinder, matching: find.byType(DismissibleActivity)); + expect(listChildren, findsNWidgets(_activities.length)); + }); - testWidgets( - "Submitting text input adds a comment with the text", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Submitting text input adds a comment with the text", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); + when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); - final textField = find.byType(TextField); - await tester.enterText(textField, 'Test comment'); - await tester.testTextInput.receiveAction(TextInputAction.done); + final textField = find.byType(TextField); + await tester.enterText(textField, 'Test comment'); + await tester.testTextInput.receiveAction(TextInputAction.done); - verify(() => activityMock.addComment('Test comment')); - }, - ); + verify(() => activityMock.addComment('Test comment')); + }); - testWidgets( - "Owner can remove all activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Owner can remove all activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect(deletableActivityFinder, findsNWidgets(_activities.length)); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.length)); + }); - testWidgets( - "Non-Owner can remove only their activities", - (tester) async { - final mockCurrentUser = MockCurrentUserProvider(); + testWidgets("Non-Owner can remove only their activities", (tester) async { + final mockCurrentUser = MockCurrentUserProvider(); - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: [ - ...overrides, - currentUserProvider.overrideWith((ref) => mockCurrentUser), - ], - ); - mockCurrentUser.state = UserStub.user1; - await tester.pumpAndSettle(); + await tester.pumpConsumerWidget( + const ActivitiesPage(), + overrides: [...overrides, currentUserProvider.overrideWith((ref) => mockCurrentUser)], + ); + mockCurrentUser.state = UserStub.user1; + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect( - deletableActivityFinder, - findsNWidgets( - _activities.where((a) => a.user == UserStub.user1).length, - ), - ); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.where((a) => a.user == UserStub.user1).length)); + }); }); } diff --git a/mobile/test/modules/activity/activity_provider_test.dart b/mobile/test/modules/activity/activity_provider_test.dart index 9bac84bab9..7964b43cad 100644 --- a/mobile/test/modules/activity/activity_provider_test.dart +++ b/mobile/test/modules/activity/activity_provider_test.dart @@ -26,19 +26,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.admin, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.admin), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -70,11 +59,7 @@ void main() { // Init and wait for providers future to complete provider = albumActivityProvider('test-album', 'test-asset'); listener = ListenerMock(); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); await container.read(provider.future); }); @@ -83,18 +68,14 @@ void main() { verifyInOrder([ () => listener.call(null, const AsyncLoading()), () => listener.call( - const AsyncLoading(), - any( - that: allOf( - [ - isA>>(), - predicate( - (AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e)), - ), - ], - ), - ), - ), + const AsyncLoading(), + any( + that: allOf([ + isA>>(), + predicate((AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e))), + ]), + ), + ), ]); verifyNoMoreInteractions(listener); @@ -102,30 +83,15 @@ void main() { group('addLike()', () { test('Like successfully added', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), ).thenAnswer((_) async => AsyncData(like)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(5)); @@ -136,31 +102,14 @@ void main() { }); test('Like failed', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ).thenAnswer( - (_) async => AsyncError(Exception('Mock'), StackTrace.current), - ); + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), + ).thenAnswer((_) async => AsyncError(Exception('Mock'), StackTrace.current)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(4)); @@ -174,16 +123,11 @@ void main() { await container.read(provider.notifier).removeActivity('3'); - verify( - () => activityMock.removeActivity('3'), - ); + verify(() => activityMock.removeActivity('3')); final activities = await container.read(provider.future); expect(activities, hasLength(3)); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '3'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '3')))); verifyNever(() => activityStatisticsMock.removeActivity()); }); @@ -195,10 +139,7 @@ void main() { final activities = await container.read(provider.future); expect(activities, hasLength(4)); - expect( - activities, - anyElement(predicate((Activity a) => a.id == '3')), - ); + expect(activities, anyElement(predicate((Activity a) => a.id == '3'))); }); test('Comment successfully removed', () async { @@ -207,10 +148,7 @@ void main() { await container.read(provider.notifier).removeActivity('1'); final activities = await container.read(provider.future); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '1'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '1')))); verify(() => activityStatisticsMock.removeActivity()); }); @@ -281,11 +219,7 @@ void main() { ); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, comment: 'Test-Comment'), ).thenAnswer((_) async => AsyncData(comment)); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); when(() => activityMock.getAllActivities('test-album')).thenAnswer((_) async => [..._activities]); @@ -294,12 +228,7 @@ void main() { await container.read(albumProvider.notifier).addComment('Test-Comment'); verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - assetId: null, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, assetId: null, comment: 'Test-Comment'), ); final activities = await container.read(albumProvider.future); @@ -327,9 +256,7 @@ void main() { assetId: 'test-asset', comment: 'Test-Comment', ), - ).thenAnswer( - (_) async => AsyncError(Exception('Error'), StackTrace.current), - ); + ).thenAnswer((_) async => AsyncError(Exception('Error'), StackTrace.current)); await container.read(provider.notifier).addComment('Test-Comment'); diff --git a/mobile/test/modules/activity/activity_statistics_provider_test.dart b/mobile/test/modules/activity/activity_statistics_provider_test.dart index 0216528ddd..7fe73868f5 100644 --- a/mobile/test/modules/activity/activity_statistics_provider_test.dart +++ b/mobile/test/modules/activity/activity_statistics_provider_test.dart @@ -15,11 +15,7 @@ void main() { setUp(() async { activityMock = ActivityServiceMock(); - container = TestUtils.createContainer( - overrides: [ - activityServiceProvider.overrideWith((ref) => activityMock), - ], - ); + container = TestUtils.createContainer(overrides: [activityServiceProvider.overrideWith((ref) => activityMock)]); listener = ListenerMock(); }); @@ -31,34 +27,21 @@ void main() { // Read here to make the getStatistics call container.read(activityStatisticsProvider('test-album', 'test-asset')); - container.listen( - activityStatisticsProvider('test-album', 'test-asset'), - listener.call, - fireImmediately: true, - ); + container.listen(activityStatisticsProvider('test-album', 'test-asset'), listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); - verifyInOrder([ - () => listener.call(null, 0), - () => listener.call(0, 5), - ]); + verifyInOrder([() => listener.call(null, 0), () => listener.call(0, 5)]); verifyNoMoreInteractions(listener); }); test('Adds activity', () async { - when( - () => activityMock.getStatistics('test-album'), - ).thenAnswer((_) async => const ActivityStats(comments: 10)); + when(() => activityMock.getStatistics('test-album')).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('test-album'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); @@ -75,11 +58,7 @@ void main() { ).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('new-album', 'test-asset'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); diff --git a/mobile/test/modules/activity/activity_text_field_test.dart b/mobile/test/modules/activity/activity_text_field_test.dart index e74099cdcd..1163330c54 100644 --- a/mobile/test/modules/activity/activity_text_field_test.dart +++ b/mobile/test/modules/activity/activity_text_field_test.dart @@ -49,12 +49,7 @@ void main() { }); testWidgets('Returns an Input text field', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(TextField), findsOneWidget); }); @@ -63,76 +58,38 @@ void main() { final userProvider = MockCurrentUserProvider(); await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: [ - currentUserProvider.overrideWith((ref) => userProvider), - ...overrides, - ], + ActivityTextField(onSubmit: (_) {}), + overrides: [currentUserProvider.overrideWith((ref) => userProvider), ...overrides], ); expect(find.byType(UserCircleAvatar), findsNothing); }); testWidgets('UserCircleAvatar displayed when user != null', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(UserCircleAvatar), findsOneWidget); }); - testWidgets( - 'Filled icon if likedId != null', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: '1', - ), - overrides: overrides, - ); - - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsNothing, - ); - }, - ); - - testWidgets('Bordered icon if likedId == null', (tester) async { + testWidgets('Filled icon if likedId != null', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), + ActivityTextField(onSubmit: (_) {}, likeId: '1'), overrides: overrides, ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsNothing, - ); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsNothing); + }); + + testWidgets('Bordered icon if likedId == null', (tester) async { + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); + + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsNothing); }); testWidgets('Adds new like', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); when(() => activityMock.addLike()).thenAnswer((_) => Future.value()); @@ -144,10 +101,7 @@ void main() { testWidgets('Removes like if already liked', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (_) {}, likeId: 'test-suffix'), overrides: overrides, ); @@ -163,10 +117,7 @@ void main() { String? receivedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receivedText = text, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receivedText = text, likeId: 'test-suffix'), overrides: overrides, ); @@ -180,11 +131,7 @@ void main() { String? receviedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receviedText = text, - isEnabled: false, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receviedText = text, isEnabled: false, likeId: 'test-suffix'), overrides: overrides, ); diff --git a/mobile/test/modules/activity/activity_tile_test.dart b/mobile/test/modules/activity/activity_tile_test.dart index fddbb6269c..eb4bb25848 100644 --- a/mobile/test/modules/activity/activity_tile_test.dart +++ b/mobile/test/modules/activity/activity_tile_test.dart @@ -43,14 +43,7 @@ void main() { testWidgets('Returns a ListTile', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -59,14 +52,7 @@ void main() { testWidgets('No trailing widget when activity assetId == null', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -77,13 +63,7 @@ void main() { testWidgets('Asset Thumbanil as trailing widget when activity assetId != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -96,13 +76,7 @@ void main() { testWidgets('No trailing widget when current asset != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -115,37 +89,23 @@ void main() { }); group('Like Activity', () { - final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ); + final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); testWidgets('Like contains filled heart as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); // Leading widget should not be null final listTile = tester.widget(find.byType(ListTile)); expect(listTile.leading, isNotNull); // And should have a favorite icon - final favoIconFinder = find.widgetWithIcon( - listTile.leading!.runtimeType, - Icons.favorite_rounded, - ); + final favoIconFinder = find.widgetWithIcon(listTile.leading!.runtimeType, Icons.favorite_rounded); expect(favoIconFinder, findsOneWidget); }); testWidgets('Like title is center aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -153,10 +113,7 @@ void main() { }); testWidgets('No subtitle for likes', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -174,10 +131,7 @@ void main() { ); testWidgets('Comment contains User Circle Avatar as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final userAvatarFinder = find.byType(UserCircleAvatar); expect(userAvatarFinder, findsOneWidget); @@ -192,10 +146,7 @@ void main() { }); testWidgets('Comment title is top aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -203,21 +154,12 @@ void main() { }); testWidgets('Contains comment text as subtitle', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); expect(listTile.subtitle, isNotNull); - expect( - find.descendant( - of: find.byType(ListTile), - matching: find.text(activity.comment!), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(ListTile), matching: find.text(activity.comment!)), findsOneWidget); }); }); } diff --git a/mobile/test/modules/activity/dismissible_activity_test.dart b/mobile/test/modules/activity/dismissible_activity_test.dart index 8cc81a69cf..e5f6258ee9 100644 --- a/mobile/test/modules/activity/dismissible_activity_test.dart +++ b/mobile/test/modules/activity/dismissible_activity_test.dart @@ -15,12 +15,7 @@ import '../../test_utils.dart'; import '../../widget_tester_extensions.dart'; import '../asset_viewer/asset_viewer_mocks.dart'; -final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, -); +final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); void main() { late MockCurrentAssetProvider assetProvider; @@ -34,10 +29,7 @@ void main() { }); testWidgets('Returns a Dismissible', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); expect(find.byType(Dismissible), findsOneWidget); }); @@ -58,11 +50,7 @@ void main() { testWidgets('Ok action in ConfirmDialog should call onDismiss with activityId', (tester) async { String? receivedActivityId; await tester.pumpConsumerWidget( - DismissibleActivity( - '1', - ActivityTile(activity), - onDismiss: (id) => receivedActivityId = id, - ), + DismissibleActivity('1', ActivityTile(activity), onDismiss: (id) => receivedActivityId = id), overrides: overrides, ); @@ -91,10 +79,7 @@ void main() { }); testWidgets('No delete dialog if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(500, 0)); @@ -104,10 +89,7 @@ void main() { }); testWidgets('No icon for background if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(-500, 0)); diff --git a/mobile/test/modules/album/album_sort_by_options_provider_test.dart b/mobile/test/modules/album/album_sort_by_options_provider_test.dart index bf3163f00d..a35255bc21 100644 --- a/mobile/test/modules/album/album_sort_by_options_provider_test.dart +++ b/mobile/test/modules/album/album_sort_by_options_provider_test.dart @@ -22,12 +22,7 @@ void main() { db = await TestUtils.initIsar(); }); - final albums = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final albums = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; setUp(() { db.writeTxnSync(() { @@ -48,23 +43,13 @@ void main() { const created = AlbumSortMode.created; test("Created time - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created time - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -73,23 +58,13 @@ void main() { const assetCount = AlbumSortMode.assetCount; test("Asset Count - ASC", () { final sorted = assetCount.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Asset Count - DESC", () { final sorted = assetCount.sortFn(albums, true); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -98,23 +73,13 @@ void main() { const lastModified = AlbumSortMode.lastModified; test("Last modified - ASC", () { final sorted = lastModified.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Last modified - DESC", () { final sorted = lastModified.sortFn(albums, true); - final sortedList = [ - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -123,23 +88,13 @@ void main() { const created = AlbumSortMode.created; test("Created - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -148,15 +103,12 @@ void main() { const mostRecent = AlbumSortMode.mostRecent; test("Most Recent - DESC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - false, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], false); final sortedList = [ AlbumStub.create2020end2026Album, AlbumStub.create2020end2024Album, @@ -167,15 +119,12 @@ void main() { }); test("Most Recent - ASC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - true, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], true); final sortedList = [ AlbumStub.create2020end2020Album, AlbumStub.create2020end2022Album, @@ -191,23 +140,13 @@ void main() { test("Most Oldest - ASC", () { final sorted = mostOldest.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Most Oldest - DESC", () { final sorted = mostOldest.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -221,67 +160,43 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort mode when none set', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); expect(container.read(albumSortByOptionsProvider), AlbumSortMode.created); }); test('Returns the correct sort mode with index from Store', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(3); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(3); - expect( - container.read(albumSortByOptionsProvider), - AlbumSortMode.lastModified, - ); + expect(container.read(albumSortByOptionsProvider), AlbumSortMode.lastModified); }); test('Properly saves the correct store index of sort mode', () { container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - AlbumSortMode.mostOldest.storeIndex, - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, AlbumSortMode.mostOldest.storeIndex), ); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); final listener = ListenerMock(); - container.listen( - albumSortByOptionsProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortByOptionsProvider, listener.call, fireImmediately: true); // Created -> Most Oldest container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); @@ -309,28 +224,18 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort order when none set - false', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); expect(container.read(albumSortOrderProvider), isFalse); }); @@ -338,25 +243,14 @@ void main() { test('Properly saves the correct order', () { container.read(albumSortOrderProvider.notifier).changeSortDirection(true); - verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - true, - ), - ); + verify(() => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, true)); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); final listener = ListenerMock(); - container.listen( - albumSortOrderProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortOrderProvider, listener.call, fireImmediately: true); // false -> true container.read(albumSortOrderProvider.notifier).changeSortDirection(true); diff --git a/mobile/test/modules/extensions/asset_extensions_test.dart b/mobile/test/modules/extensions/asset_extensions_test.dart index a164b9ddce..2b9b740ca7 100644 --- a/mobile/test/modules/extensions/asset_extensions_test.dart +++ b/mobile/test/modules/extensions/asset_extensions_test.dart @@ -5,21 +5,11 @@ import 'package:immich_mobile/extensions/asset_extensions.dart'; import 'package:timezone/data/latest.dart'; import 'package:timezone/timezone.dart'; -ExifInfo makeExif({ - DateTime? dateTimeOriginal, - String? timeZone, -}) { - return ExifInfo( - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - ); +ExifInfo makeExif({DateTime? dateTimeOriginal, String? timeZone}) { + return ExifInfo(dateTimeOriginal: dateTimeOriginal, timeZone: timeZone); } -Asset makeAsset({ - required String id, - required DateTime createdAt, - ExifInfo? exifInfo, -}) { +Asset makeAsset({required String id, required DateTime createdAt, ExifInfo? exifInfo}) { return Asset( checksum: '', localId: id, @@ -79,10 +69,7 @@ void main() { test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', () { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); - final e = makeExif( - dateTimeOriginal: dateTimeOriginal, - timeZone: "#_#", - ); // Invalid timezone + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: "#_#"); // Invalid timezone final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); diff --git a/mobile/test/modules/extensions/builtin_extensions_test.dart b/mobile/test/modules/extensions/builtin_extensions_test.dart index 2de450a952..e52362f3d3 100644 --- a/mobile/test/modules/extensions/builtin_extensions_test.dart +++ b/mobile/test/modules/extensions/builtin_extensions_test.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/string_extensions.dart'; void main() { group('Test toDuration', () { test('ok', () { - expect( - "1:02:33".toDuration(), - const Duration(hours: 1, minutes: 2, seconds: 33), - ); + expect("1:02:33".toDuration(), const Duration(hours: 1, minutes: 2, seconds: 33)); }); test('malformed', () { expect("".toDuration(), isNull); @@ -45,9 +42,7 @@ void main() { test('withKey', () { final a = ["a", "bb", "cc", "ddd"]; expect( - a.uniqueConsecutive( - compare: (s1, s2) => s1.length.compareTo(s2.length), - ), + a.uniqueConsecutive(compare: (s1, s2) => s1.length.compareTo(s2.length)), orderedEquals(["a", "bb", "ddd"]), ); }); diff --git a/mobile/test/modules/home/asset_grid_data_structure_test.dart b/mobile/test/modules/home/asset_grid_data_structure_test.dart index b4ee851969..3e1fe06c68 100644 --- a/mobile/test/modules/home/asset_grid_data_structure_test.dart +++ b/mobile/test/modules/home/asset_grid_data_structure_test.dart @@ -58,10 +58,7 @@ void main() { group('Test grouped', () { test('test grouped check months', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 @@ -75,33 +72,18 @@ void main() { // Day 1 // 5 Assets => 2 Rows expect(renderList.elements, hasLength(4)); - expect( - renderList.elements[0].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[0].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[0].date.month, 1); - expect( - renderList.elements[1].type, - RenderAssetGridElementType.groupDividerTitle, - ); + expect(renderList.elements[1].type, RenderAssetGridElementType.groupDividerTitle); expect(renderList.elements[1].date.month, 1); - expect( - renderList.elements[2].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[2].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[2].date.month, 2); - expect( - renderList.elements[3].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[3].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[3].date.month, 10); }); test('test grouped check types', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 diff --git a/mobile/test/modules/map/map_theme_override_test.dart b/mobile/test/modules/map/map_theme_override_test.dart index 78fa6b0043..de16b7f24f 100644 --- a/mobile/test/modules/map/map_theme_override_test.dart +++ b/mobile/test/modules/map/map_theme_override_test.dart @@ -67,9 +67,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - darkStyleFetched: const AsyncError("Error", StackTrace.empty), - ); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncError("Error", StackTrace.empty)); await tester.pumpAndSettle(); expect(mapStyle?.hasError, isTrue); }); @@ -86,10 +84,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - themeMode: ThemeMode.light, - lightStyleFetched: const AsyncData("light"), - ); + mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); }); diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 2858e6a9e7..b51a4d67fd 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -66,13 +66,7 @@ void main() { final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); - final owner = UserDto( - id: "1", - updatedAt: DateTime.now(), - email: "a@b.c", - name: "first last", - isAdmin: false, - ); + final owner = UserDto(id: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", isAdmin: false); late SyncService s; setUpAll(() async { WidgetsFlutterBinding.ensureInitialized(); @@ -81,10 +75,7 @@ void main() { db.writeTxnSync(() => db.clearSync()); await StoreService.init(storeRepository: IsarStoreRepository(db)); await Store.put(StoreKey.currentUser, owner); - await LogService.init( - logRepository: IsarLogRepository(db), - storeRepository: IsarStoreRepository(db), - ); + await LogService.init(logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db)); }); final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), @@ -119,22 +110,20 @@ void main() { when(() => userRepository.getAll(sortBy: SortUserBy.id)).thenAnswer((_) async => [owner]); when(() => userRepository.getAll()).thenAnswer((_) async => [owner]); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => initialAssets); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[3], null, null]); + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[3], null, null]); when(() => assetRepository.updateAll(any())).thenAnswer((_) async => []); when(() => assetRepository.deleteByIds(any())).thenAnswer((_) async {}); when(() => exifInfoRepository.updateAll(any())).thenAnswer((_) async => []); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]); registerFallbackValue(Direction.sharedByMe); when(() => partnerApiRepository.getAll(any())).thenAnswer((_) async => []); @@ -170,9 +159,7 @@ void main() { ); expect(c1, isTrue); final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]); - verify( - () => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]), - ); + verify(() => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset])); }); test('test syncing duplicate assets', () async { @@ -191,10 +178,7 @@ void main() { ); expect(c1, isTrue); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => remoteAssets); final bool c2 = await s.syncRemoteAssetsToDb( users: [owner], @@ -204,10 +188,7 @@ void main() { expect(c2, isFalse); final currentState = [...remoteAssets]; when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => currentState); remoteAssets.removeAt(4); final bool c3 = await s.syncRemoteAssetsToDb( @@ -228,18 +209,19 @@ void main() { test('test efficient sync', () async { when( - () => assetRepository.deleteAllByRemoteId( - [initialAssets[1].remoteId!, initialAssets[2].remoteId!], - state: AssetState.remote, - ), + () => assetRepository.deleteAllByRemoteId([ + initialAssets[1].remoteId!, + initialAssets[2].remoteId!, + ], state: AssetState.remote), ).thenAnswer((_) async { return; }); when( () => assetRepository.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), ).thenAnswer((_) async => [initialAssets[2]]); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[0], null, null]); //afg + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[0], null, null]); //afg final List toUpsert = [ makeAsset(checksum: "a", remoteId: "0-1"), // changed makeAsset(checksum: "f", remoteId: "0-2"), // new @@ -262,18 +244,11 @@ void main() { test('test upsert with EXIF data', () async { final assets = [AssetStub.image1, AssetStub.image2]; - expect( - assets.map((a) => a.exifInfo?.assetId), - List.filled(assets.length, null), - ); + expect(assets.map((a) => a.exifInfo?.assetId), List.filled(assets.length, null)); await s.upsertAssetsWithExif(assets); verify( () => exifInfoRepository.updateAll( - any( - that: containsAll( - assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)), - ), - ), + any(that: containsAll(assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)))), ), ); expect(assets.map((a) => a.exifInfo?.assetId), assets.map((a) => a.id)); @@ -282,8 +257,4 @@ void main() { }); } -Future<(List?, List?)> _failDiff( - List user, - DateTime time, -) => - Future.value((null, null)); +Future<(List?, List?)> _failDiff(List user, DateTime time) => Future.value((null, null)); diff --git a/mobile/test/modules/utils/async_mutex_test.dart b/mobile/test/modules/utils/async_mutex_test.dart index c1b39035b4..d50567721b 100644 --- a/mobile/test/modules/utils/async_mutex_test.dart +++ b/mobile/test/modules/utils/async_mutex_test.dart @@ -7,33 +7,13 @@ void main() { AsyncMutex lock = AsyncMutex(); List events = []; expect(0, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(1), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1))); expect(1, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 3), - () => events.add(2), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2))); expect(2, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 1), - () => events.add(3), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3))); expect(3, lock.enqueued); - await lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(4), - ), - ); + await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); expect(0, lock.enqueued); expect(events, [1, 2, 3, 4]); }); diff --git a/mobile/test/modules/utils/thumbnail_utils_test.dart b/mobile/test/modules/utils/thumbnail_utils_test.dart index 6fa0127f16..dd4588fc80 100644 --- a/mobile/test/modules/utils/thumbnail_utils_test.dart +++ b/mobile/test/modules/utils/thumbnail_utils_test.dart @@ -9,22 +9,12 @@ void main() { final dateTimeString = DateFormat.yMMMMd().format(dateTime); test('returns description if it has one', () { - final result = getAltText( - const ExifInfo(description: 'description'), - dateTime, - AssetType.image, - [], - ); + final result = getAltText(const ExifInfo(description: 'description'), dateTime, AssetType.image, []); expect(result, 'description'); }); test('returns image alt text with date if no location', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - [], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, []); expect(template, "image_alt_text_date"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); @@ -45,12 +35,7 @@ void main() { }); test('returns image alt text with date and some people', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - ["Alice", "Bob"], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, ["Alice", "Bob"]); expect(template, "image_alt_text_date_2_people"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); diff --git a/mobile/test/modules/utils/version_compatibility_test.dart b/mobile/test/modules/utils/version_compatibility_test.dart index bdcc0c8fce..82f89237c0 100644 --- a/mobile/test/modules/utils/version_compatibility_test.dart +++ b/mobile/test/modules/utils/version_compatibility_test.dart @@ -6,10 +6,7 @@ void main() { String? result; result = getVersionCompatibilityMessage(1, 0, 2, 0); - expect( - result, - 'Your app major version is not compatible with the server!', - ); + expect(result, 'Your app major version is not compatible with the server!'); result = getVersionCompatibilityMessage(1, 106, 1, 105); expect( diff --git a/mobile/test/pages/search/search.page_test.dart b/mobile/test/pages/search/search.page_test.dart index 1c1a410150..9592623a28 100644 --- a/mobile/test/pages/search/search.page_test.dart +++ b/mobile/test/pages/search/search.page_test.dart @@ -45,18 +45,11 @@ void main() { final emptyTextSearch = isA().having((s) => s.originalFileName, 'originalFileName', null); testWidgets('contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.abc_rounded), - findsOneWidget, - reason: 'Should have contextual search icon', - ); + expect(find.byIcon(Icons.abc_rounded), findsOneWidget, reason: 'Should have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -64,14 +57,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchSmart(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchSmart(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.query, 'query', 'test'), - ); + expect(captured.first, isA().having((s) => s.query, 'query', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); @@ -81,10 +69,7 @@ void main() { }); testWidgets('not contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); @@ -92,11 +77,7 @@ void main() { await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.image_search_rounded), - findsOneWidget, - reason: 'Should not have contextual search icon', - ); + expect(find.byIcon(Icons.image_search_rounded), findsOneWidget, reason: 'Should not have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -104,14 +85,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchAssets(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchAssets(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.originalFileName, 'originalFileName', 'test'), - ); + expect(captured.first, isA().having((s) => s.originalFileName, 'originalFileName', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart index 547b049593..97683cdab1 100644 --- a/mobile/test/services/album.service_test.dart +++ b/mobile/test/services/album.service_test.dart @@ -33,12 +33,12 @@ void main() { when(() => userService.getMyUser()).thenReturn(UserStub.user1); - when(() => albumRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => albumRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); sut = AlbumService( syncService, @@ -66,15 +66,14 @@ void main() { test('one selected albums, two on device', () async { when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); + when( + () => backupRepository.getIdsBySelection(BackupSelection.select), + ).thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); when(() => albumMediaRepository.getAll()).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, true); - verify( - () => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null), - ).called(1); + verify(() => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null)).called(1); verifyNoMoreInteractions(syncService); }); }); @@ -85,15 +84,12 @@ void main() { when(() => syncService.syncUsersFromServer(any())).thenAnswer((_) async => true); when(() => albumApiRepository.getAll(shared: true)).thenAnswer((_) async => [AlbumStub.sharedWithUser]); - when(() => albumApiRepository.getAll(shared: null)) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when( + () => albumApiRepository.getAll(shared: null), + ).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); when( - () => syncService.syncRemoteAlbumsToDb([ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).thenAnswer((_) async => true); final result = await sut.refreshRemoteAlbums(); expect(result, true); @@ -102,13 +98,7 @@ void main() { verify(() => albumApiRepository.getAll(shared: true)).called(1); verify(() => albumApiRepository.getAll(shared: null)).called(1); verify( - () => syncService.syncRemoteAlbumsToDb( - [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ], - ), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).called(1); verifyNoMoreInteractions(userService); verifyNoMoreInteractions(albumApiRepository); @@ -130,9 +120,7 @@ void main() { () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.create(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.twoAsset); + when(() => albumRepository.create(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.twoAsset); final result = await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); expect(result, AlbumStub.twoAsset); @@ -143,9 +131,7 @@ void main() { sharedUserIds: [UserStub.user1.id], ), ).called(1); - verify( - () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), - ).called(1); + verify(() => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset)).called(1); }); }); @@ -153,29 +139,14 @@ void main() { test('one added, one duplicate', () async { when( () => albumApiRepository.addAssets(AlbumStub.oneAsset.remoteId!, any()), - ).thenAnswer( - (_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!]), - ); - when( - () => albumRepository.get(AlbumStub.oneAsset.id), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2]), - ).thenAnswer((_) async {}); - when( - () => albumRepository.removeAssets(AlbumStub.oneAsset, []), - ).thenAnswer((_) async {}); - when( - () => albumRepository.recalculateMetadata(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.update(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); + ).thenAnswer((_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!])); + when(() => albumRepository.get(AlbumStub.oneAsset.id)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2])).thenAnswer((_) async {}); + when(() => albumRepository.removeAssets(AlbumStub.oneAsset, [])).thenAnswer((_) async {}); + when(() => albumRepository.recalculateMetadata(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.update(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); - final result = await sut.addAssets( - AlbumStub.oneAsset, - [AssetStub.image1, AssetStub.image2], - ); + final result = await sut.addAssets(AlbumStub.oneAsset, [AssetStub.image1, AssetStub.image2]); expect(result != null, true); expect(result!.alreadyInAlbum, [AssetStub.image1.remoteId!]); @@ -187,9 +158,7 @@ void main() { test('one added', () async { when( () => albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), - ).thenAnswer( - (_) async => AlbumStub.sharedWithUser, - ); + ).thenAnswer((_) async => AlbumStub.sharedWithUser); when( () => albumRepository.addUsers( @@ -198,14 +167,9 @@ void main() { ), ).thenAnswer((_) async => AlbumStub.emptyAlbum); - when( - () => albumRepository.update(AlbumStub.emptyAlbum), - ).thenAnswer((_) async => AlbumStub.emptyAlbum); + when(() => albumRepository.update(AlbumStub.emptyAlbum)).thenAnswer((_) async => AlbumStub.emptyAlbum); - final result = await sut.addUsers( - AlbumStub.emptyAlbum, - [UserStub.user2.id], - ); + final result = await sut.addUsers(AlbumStub.emptyAlbum, [UserStub.user2.id]); expect(result, true); }); diff --git a/mobile/test/services/asset.service_test.dart b/mobile/test/services/asset.service_test.dart index 5077764f26..b741150165 100644 --- a/mobile/test/services/asset.service_test.dart +++ b/mobile/test/services/asset.service_test.dart @@ -81,9 +81,7 @@ void main() { final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; - final receivedDatetime = receivedAssets.cast().map( - (a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0), - ); + final receivedDatetime = receivedAssets.cast().map((a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0)); expect(receivedDatetime.every((d) => d == dateTime), isTrue); }); @@ -97,8 +95,8 @@ void main() { upsertExifCallback.called(1); final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; final receivedCoords = receivedAssets.cast().map( - (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), - ); + (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), + ); expect(receivedCoords.every((l) => l == latLng), isTrue); }); }); diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index c9b44fe28b..1bad780ca7 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -95,10 +95,7 @@ void main() { when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Invalid URL')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -109,10 +106,7 @@ void main() { when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Server is not reachable')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -126,10 +120,7 @@ void main() { when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); when( - () => appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ), + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), ).thenAnswer((_) => Future.value(null)); await sut.logout(); @@ -144,10 +135,7 @@ void main() { when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); when( - () => appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ), + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), ).thenAnswer((_) => Future.value(null)); await sut.logout(); @@ -176,8 +164,9 @@ void main() { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenAnswer((_) async => 'http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenAnswer((_) async => 'http://local.endpoint'); final result = await sut.setOpenApiServiceEndpoint(); @@ -192,12 +181,9 @@ void main() { test('Should set external endpoint if wifi name not matching', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenAnswer((_) async => 'https://external.endpoint/api'); @@ -209,23 +195,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); test('Should set second external endpoint if the first throw any error', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -242,23 +220,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should set second external endpoint if the first throw ApiException', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -275,17 +245,16 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should handle error when setting local connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenThrow(Exception('Local endpoint error')); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenThrow(Exception('Local endpoint error')); final result = await sut.setOpenApiServiceEndpoint(); @@ -300,12 +269,9 @@ void main() { test('Should handle error when setting external connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenThrow(Exception('External endpoint error')); @@ -317,9 +283,7 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); }); } diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 7aab7f9428..64b9fc604b 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -22,23 +22,22 @@ void main() { group('fillAlbumWithDatabaseEntities', () { test('remote album with owner, thumbnail, sharedUsers and assets', () async { - final Album album = Album( - name: "album-with-two-assets-and-two-users", - localId: "album-with-two-assets-and-two-users-local", - remoteId: "album-with-two-assets-and-two-users-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: true, - activityEnabled: true, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..remoteThumbnailAssetId = AssetStub.image1.remoteId - ..assets.addAll([AssetStub.image1, AssetStub.image1]) - ..owner.value = User.fromDto(UserStub.user1) - ..sharedUsers.addAll( - [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)], - ); + final Album album = + Album( + name: "album-with-two-assets-and-two-users", + localId: "album-with-two-assets-and-two-users-local", + remoteId: "album-with-two-assets-and-two-users-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: true, + activityEnabled: true, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..remoteThumbnailAssetId = AssetStub.image1.remoteId + ..assets.addAll([AssetStub.image1, AssetStub.image1]) + ..owner.value = User.fromDto(UserStub.user1) + ..sharedUsers.addAll([User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)]); when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); @@ -52,23 +51,20 @@ void main() { await sut.fillAlbumWithDatabaseEntities(album); expect(album.owner.value?.toDto(), UserStub.admin); expect(album.thumbnail.value, AssetStub.image1); - expect( - album.remoteUsers.map((u) => u.toDto()).toSet(), - {UserStub.user1, UserStub.user2}, - ); + expect(album.remoteUsers.map((u) => u.toDto()).toSet(), {UserStub.user1, UserStub.user2}); expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2}); }); test('remote album without any info', () async { makeEmptyAlbum() => Album( - name: "album-without-info", - localId: "album-without-info-local", - remoteId: "album-without-info-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - ); + name: "album-without-info", + localId: "album-without-info-local", + remoteId: "album-without-info-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + ); final album = makeEmptyAlbum(); await sut.fillAlbumWithDatabaseEntities(album); diff --git a/mobile/test/services/hash_service_test.dart b/mobile/test/services/hash_service_test.dart index 5360e30341..74b8575e40 100644 --- a/mobile/test/services/hash_service_test.dart +++ b/mobile/test/services/hash_service_test.dart @@ -31,15 +31,10 @@ void main() { mockBackgroundService = MockBackgroundService(); mockDeviceAssetRepository = MockDeviceAssetRepository(); - sut = HashService( - deviceAssetRepository: mockDeviceAssetRepository, - backgroundService: mockBackgroundService, - ); + sut = HashService(deviceAssetRepository: mockDeviceAssetRepository, backgroundService: mockBackgroundService); when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); }); @@ -53,17 +48,13 @@ void main() { when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); // No DB entries for this asset - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer((_) async => []); final result = await sut.hashAssets([mockAsset]); // Verify we stored the new hash in DB when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( @@ -73,10 +64,7 @@ void main() { ).called(1); verify(() => mockDeviceAssetRepository.deleteIds([])).called(1); }); - expect( - result, - [AssetStub.image1.copyWith(checksum: base64.encode(hash))], - ); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -84,15 +72,9 @@ void main() { test("when the asset is not modified", () async { final hash = utf8.encode("image1-hash"); - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer( + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer( (_) async => [ - DeviceAsset( - assetId: AssetStub.image1.localId!, - hash: hash, - modifiedTime: AssetStub.image1.fileModifiedAt, - ), + DeviceAsset(assetId: AssetStub.image1.localId!, hash: hash, modifiedTime: AssetStub.image1.fileModifiedAt), ], ); final result = await sut.hashAssets([AssetStub.image1]); @@ -102,9 +84,7 @@ void main() { verifyNever(() => mockDeviceAssetRepository.updateAll(any())); verifyNever(() => mockDeviceAssetRepository.deleteIds(any())); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); test("hashed successful when asset is modified", () async { @@ -118,9 +98,7 @@ void main() { final result = await sut.hashAssets([mockAsset]); when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( @@ -133,9 +111,7 @@ void main() { verify(() => mockBackgroundService.digestFiles([file.path])).called(1); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -161,18 +137,14 @@ void main() { verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockBackgroundService.digestFile(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); expect(result, isEmpty); }); test("cleanups DeviceAsset when hashing failed", () async { when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); @@ -194,9 +166,7 @@ void main() { // To avoid this, we capture the callback and execute it within the transaction stub itself // and verify the results inside the transaction stub verify(() => mockDeviceAssetRepository.updateAll([])).called(1); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); }); when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer( @@ -243,14 +213,11 @@ void main() { verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("processes assets in batches when file limit is reached", () async { @@ -283,14 +250,11 @@ void main() { verify(() => mockBackgroundService.digestFiles([file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("HashService: Sort & Process different states", () async { @@ -345,15 +309,10 @@ void main() { test("handles all file access failures", () async { // No DB entries when( - () => mockDeviceAssetRepository.getByIds( - [AssetStub.image1.localId!, AssetStub.image2.localId!], - ), + () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!, AssetStub.image2.localId!]), ).thenAnswer((_) async => []); - final result = await sut.hashAssets([ - AssetStub.image1, - AssetStub.image2, - ]); + final result = await sut.hashAssets([AssetStub.image1, AssetStub.image2]); verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); @@ -363,9 +322,7 @@ void main() { }); } -Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( - Asset asset, -) async { +Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock(Asset asset) async { final random = Random(); final hash = Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); final mockAsset = MockAsset(); @@ -384,8 +341,9 @@ Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( when(() => mockAsset.fileName).thenReturn(asset.fileName); when(() => mockAsset.fileCreatedAt).thenReturn(asset.fileCreatedAt); when(() => mockAsset.fileModifiedAt).thenReturn(asset.fileModifiedAt); - when(() => mockAsset.copyWith(checksum: any(named: "checksum"))) - .thenReturn(asset.copyWith(checksum: base64.encode(hash))); + when( + () => mockAsset.copyWith(checksum: any(named: "checksum")), + ).thenReturn(asset.copyWith(checksum: base64.encode(hash))); when(() => mockAsset.local).thenAnswer((_) => mockAssetEntity); when(() => mockAssetEntity.originFile).thenAnswer((_) async => file); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index 596d3bcd1c..d932e2ffc7 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -73,11 +73,7 @@ abstract final class TestUtils { List overrides = const [], List? observers, }) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); + final container = ProviderContainer(parent: parent, overrides: overrides, observers: observers); // Dispose on test end addTearDown(container.dispose); @@ -94,23 +90,22 @@ abstract final class TestUtils { // Workaround till the following issue is resolved // https://github.com/dart-lang/test/issues/2307 - static T fakeAsync( - Future Function(FakeAsync _) callback, { - DateTime? initialTime, - }) { + static T fakeAsync(Future Function(FakeAsync _) callback, {DateTime? initialTime}) { late final T result; Object? error; StackTrace? stack; FakeAsync(initialTime: initialTime).run((FakeAsync async) { bool shouldPump = true; unawaited( - callback(async).then( - (value) => result = value, - onError: (e, s) { - error = e; - stack = s; - }, - ).whenComplete(() => shouldPump = false), + callback(async) + .then( + (value) => result = value, + onError: (e, s) { + error = e; + stack = s; + }, + ) + .whenComplete(() => shouldPump = false), ); while (shouldPump) { diff --git a/mobile/test/widget_tester_extensions.dart b/mobile/test/widget_tester_extensions.dart index 7d5b266224..bb3fc3f418 100644 --- a/mobile/test/widget_tester_extensions.dart +++ b/mobile/test/widget_tester_extensions.dart @@ -18,10 +18,7 @@ extension PumpConsumerWidget on WidgetTester { return pumpWidget( ProviderScope( overrides: overrides, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: Material(child: widget), - ), + child: MaterialApp(debugShowCheckedModeBanner: false, home: Material(child: widget)), ), duration: duration, phase: phase, From 34974b036c5bc79f899c44c058b618412110362a Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:52:50 +0530 Subject: [PATCH 115/169] fix: handle back gesture in multi selection mode (#20356) * fix: handle back gesture in multi selection mode # Conflicts: # mobile/lib/presentation/widgets/timeline/timeline.widget.dart * remove null-aware element because Isar * chore: set sqlite busy_timeout to 500ms (#20358) fix: add busy_timeout pragma Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../repositories/db.repository.dart | 1 + .../widgets/timeline/timeline.widget.dart | 132 ++++++++---------- 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 7f6374ed24..6e574afa8c 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -109,6 +109,7 @@ class Drift extends $Drift implements IDatabaseRepository { await customStatement('PRAGMA foreign_keys = ON'); await customStatement('PRAGMA synchronous = NORMAL'); await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); }, ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 26799580a2..d946872781 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -155,79 +155,69 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final asyncSegments = ref.watch(timelineSegmentProvider); final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); - return asyncSegments.widgetWhen( - onData: (segments) { - final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; - final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar ? 200 : 0; - final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; - - const scrubberBottomPadding = 100.0; - final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); - - return PrimaryScrollController( - controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], - ), - ), - if (!isSelectionMode) ...[ - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - ), - if (widget.bottomSheet != null) - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: widget.bottomSheet, - ), - ], - ], - ), - ); + return PopScope( + canPop: !isMultiSelectEnabled, + onPopInvokedWithResult: (_, __) { + if (isMultiSelectEnabled) { + ref.read(multiSelectProvider.notifier).reset(); + } }, + child: asyncSegments.widgetWhen( + onData: (segments) { + final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; + final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar + ? 200 + : 0; + final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; + + const scrubberBottomPadding = 100.0; + final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); + + return PrimaryScrollController( + controller: _scrollController, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), + ), + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], + ], + ), + ); + }, + ), ); } } From 7d759edfcc9842b4ddfa411d5f0bad8ea185c8b2 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 18:40:34 -0400 Subject: [PATCH 116/169] chore: add permission metadata to open-api document (#20373) --- open-api/immich-openapi-specs.json | 501 ++++++++++++++++++---------- server/src/middleware/auth.guard.ts | 6 +- 2 files changed, 339 insertions(+), 168 deletions(-) diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 5000eaa5c7..8b305aa8b5 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -77,7 +77,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.read" }, "post": { "operationId": "createActivity", @@ -117,7 +118,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.create" } }, "/activities/statistics": { @@ -168,7 +170,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.statistics" } }, "/activities/{id}": { @@ -203,7 +206,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.delete" } }, "/admin/notifications": { @@ -391,7 +395,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "post": { "operationId": "createUserAdmin", @@ -431,7 +436,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.create" } }, "/admin/users/{id}": { @@ -483,7 +489,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.delete" }, "get": { "operationId": "getUserAdmin", @@ -523,7 +530,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "put": { "operationId": "updateUserAdmin", @@ -573,7 +581,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.update" } }, "/admin/users/{id}/preferences": { @@ -615,7 +624,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "put": { "operationId": "updateUserPreferencesAdmin", @@ -665,7 +675,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.update" } }, "/admin/users/{id}/restore": { @@ -707,7 +718,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.delete" } }, "/admin/users/{id}/statistics": { @@ -773,7 +785,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" } }, "/albums": { @@ -827,7 +840,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read" }, "post": { "operationId": "createAlbum", @@ -867,7 +881,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.create" } }, "/albums/statistics": { @@ -899,7 +914,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.statistics" } }, "/albums/{id}": { @@ -934,7 +950,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.delete" }, "get": { "operationId": "getAlbumInfo", @@ -998,7 +1015,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read" }, "patch": { "operationId": "updateAlbumInfo", @@ -1048,7 +1066,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.update" } }, "/albums/{id}/assets": { @@ -1103,7 +1122,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.delete" }, "put": { "operationId": "addAssetsToAlbum", @@ -1172,7 +1192,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.create" } }, "/albums/{id}/user/{userId}": { @@ -1215,7 +1236,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.delete" }, "put": { "operationId": "updateAlbumUser", @@ -1266,7 +1288,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.update" } }, "/albums/{id}/users": { @@ -1318,7 +1341,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.create" } }, "/api-keys": { @@ -1353,7 +1377,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read" }, "post": { "operationId": "createApiKey", @@ -1393,7 +1418,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.create" } }, "/api-keys/{id}": { @@ -1428,7 +1454,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.delete" }, "get": { "operationId": "getApiKey", @@ -1468,7 +1495,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read" }, "put": { "operationId": "updateApiKey", @@ -1518,7 +1546,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.update" } }, "/assets": { @@ -1553,7 +1582,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.delete" }, "post": { "operationId": "uploadAsset", @@ -1620,7 +1650,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.upload" }, "put": { "operationId": "updateAssets", @@ -1653,7 +1684,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update" } }, "/assets/bulk-upload-check": { @@ -1873,7 +1905,8 @@ ], "x-immich-lifecycle": { "deprecatedAt": "v1.116.0" - } + }, + "x-immich-permission": "asset.read" } }, "/assets/statistics": { @@ -1930,7 +1963,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.statistics" } }, "/assets/{id}": { @@ -1988,7 +2022,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.read" }, "put": { "operationId": "updateAsset", @@ -2038,7 +2073,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update" } }, "/assets/{id}/original": { @@ -2097,7 +2133,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.download" }, "put": { "description": "Replace the asset with new file, without changing its id", @@ -2168,7 +2205,8 @@ ], "x-immich-lifecycle": { "addedAt": "v1.106.0" - } + }, + "x-immich-permission": "asset.replace" } }, "/assets/{id}/thumbnail": { @@ -2235,7 +2273,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.view" } }, "/assets/{id}/video/playback": { @@ -2294,7 +2333,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.view" } }, "/auth/admin-sign-up": { @@ -2367,7 +2407,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "auth.changePassword" } }, "/auth/login": { @@ -2465,7 +2506,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.delete" }, "post": { "operationId": "setupPinCode", @@ -2498,7 +2540,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.create" }, "put": { "operationId": "changePinCode", @@ -2531,7 +2574,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.update" } }, "/auth/session/lock": { @@ -2715,7 +2759,8 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download" } }, "/download/info": { @@ -2774,7 +2819,8 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download" } }, "/duplicates": { @@ -2809,7 +2855,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete" }, "get": { "operationId": "getAssetDuplicates", @@ -2842,7 +2889,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.read" } }, "/duplicates/{id}": { @@ -2877,7 +2925,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete" } }, "/faces": { @@ -2922,7 +2971,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.read" }, "post": { "operationId": "createFace", @@ -2955,7 +3005,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.create" } }, "/faces/{id}": { @@ -3000,7 +3051,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.delete" }, "put": { "operationId": "reassignFacesById", @@ -3050,7 +3102,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.update" } }, "/jobs": { @@ -3082,7 +3135,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.read" }, "post": { "operationId": "createJob", @@ -3115,7 +3169,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.create" } }, "/jobs/{id}": { @@ -3166,7 +3221,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.create" } }, "/libraries": { @@ -3201,7 +3257,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.read" }, "post": { "operationId": "createLibrary", @@ -3241,7 +3298,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.create" } }, "/libraries/{id}": { @@ -3276,7 +3334,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.delete" }, "get": { "operationId": "getLibrary", @@ -3316,7 +3375,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.read" }, "put": { "operationId": "updateLibrary", @@ -3366,7 +3426,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.update" } }, "/libraries/{id}/scan": { @@ -3401,7 +3462,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.update" } }, "/libraries/{id}/statistics": { @@ -3443,7 +3505,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.statistics" } }, "/libraries/{id}/validate": { @@ -3704,7 +3767,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read" }, "post": { "operationId": "createMemory", @@ -3744,7 +3808,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.create" } }, "/memories/statistics": { @@ -3810,7 +3875,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.statistics" } }, "/memories/{id}": { @@ -3845,7 +3911,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.delete" }, "get": { "operationId": "getMemory", @@ -3885,7 +3952,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read" }, "put": { "operationId": "updateMemory", @@ -3935,7 +4003,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.update" } }, "/memories/{id}/assets": { @@ -3990,7 +4059,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.delete" }, "put": { "operationId": "addMemoryAssets", @@ -4043,7 +4113,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.create" } }, "/notifications": { @@ -4078,7 +4149,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete" }, "get": { "operationId": "getNotifications", @@ -4145,7 +4217,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read" }, "put": { "operationId": "updateNotifications", @@ -4178,7 +4251,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update" } }, "/notifications/{id}": { @@ -4213,7 +4287,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete" }, "get": { "operationId": "getNotification", @@ -4253,7 +4328,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read" }, "put": { "operationId": "updateNotification", @@ -4303,7 +4379,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update" } }, "/oauth/authorize": { @@ -4497,7 +4574,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.read" } }, "/partners/{id}": { @@ -4532,7 +4610,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.delete" }, "post": { "operationId": "createPartner", @@ -4572,7 +4651,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.create" }, "put": { "operationId": "updatePartner", @@ -4622,7 +4702,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.update" } }, "/people": { @@ -4657,7 +4738,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete" }, "get": { "operationId": "getAllPeople", @@ -4737,7 +4819,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" }, "post": { "operationId": "createPerson", @@ -4777,7 +4860,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.create" }, "put": { "operationId": "updatePeople", @@ -4820,7 +4904,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update" } }, "/people/{id}": { @@ -4855,7 +4940,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete" }, "get": { "operationId": "getPerson", @@ -4895,7 +4981,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" }, "put": { "operationId": "updatePerson", @@ -4945,7 +5032,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update" } }, "/people/{id}/merge": { @@ -5000,7 +5088,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.merge" } }, "/people/{id}/reassign": { @@ -5055,7 +5144,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.reassign" } }, "/people/{id}/statistics": { @@ -5097,7 +5187,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.statistics" } }, "/people/{id}/thumbnail": { @@ -5140,7 +5231,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" } }, "/search/cities": { @@ -5175,7 +5267,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/explore": { @@ -5210,7 +5303,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/metadata": { @@ -5252,7 +5346,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/person": { @@ -5304,7 +5399,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "person.read" } }, "/search/places": { @@ -5348,7 +5444,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/random": { @@ -5393,7 +5490,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/smart": { @@ -5435,7 +5533,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/statistics": { @@ -5477,7 +5576,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.statistics" } }, "/search/suggestions": { @@ -5562,7 +5662,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/server/about": { @@ -5594,7 +5695,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.about" } }, "/server/apk-links": { @@ -5626,7 +5728,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.apkLinks" } }, "/server/config": { @@ -5693,7 +5796,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.delete" }, "get": { "operationId": "getServerLicense", @@ -5726,7 +5830,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.read" }, "put": { "operationId": "setServerLicense", @@ -5766,7 +5871,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.update" } }, "/server/media-types": { @@ -5840,7 +5946,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.statistics" } }, "/server/storage": { @@ -5872,7 +5979,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.storage" } }, "/server/theme": { @@ -5995,7 +6103,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete" }, "get": { "operationId": "getSessions", @@ -6028,7 +6137,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.read" }, "post": { "operationId": "createSession", @@ -6068,7 +6178,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.create" } }, "/sessions/{id}": { @@ -6103,7 +6214,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete" }, "put": { "operationId": "updateSession", @@ -6153,7 +6265,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.update" } }, "/sessions/{id}/lock": { @@ -6188,7 +6301,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.lock" } }, "/shared-links": { @@ -6233,7 +6347,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read" }, "post": { "operationId": "createSharedLink", @@ -6273,7 +6388,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.create" } }, "/shared-links/me": { @@ -6374,7 +6490,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.delete" }, "get": { "operationId": "getSharedLinkById", @@ -6414,7 +6531,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read" }, "patch": { "operationId": "updateSharedLink", @@ -6464,7 +6582,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.update" } }, "/shared-links/{id}/assets": { @@ -6639,7 +6758,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete" }, "get": { "operationId": "searchStacks", @@ -6682,7 +6802,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read" }, "post": { "operationId": "createStack", @@ -6722,7 +6843,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.create" } }, "/stacks/{id}": { @@ -6757,7 +6879,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete" }, "get": { "operationId": "getStack", @@ -6797,7 +6920,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read" }, "put": { "operationId": "updateStack", @@ -6847,7 +6971,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.update" } }, "/stacks/{id}/assets/{assetId}": { @@ -6891,7 +7016,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.update" } }, "/sync/ack": { @@ -6926,7 +7052,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.delete" }, "get": { "operationId": "getSyncAck", @@ -6959,7 +7086,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.read" }, "post": { "operationId": "sendSyncAck", @@ -6992,7 +7120,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.update" } }, "/sync/delta-sync": { @@ -7114,7 +7243,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "sync.stream" } }, "/system-config": { @@ -7146,7 +7276,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" }, "put": { "operationId": "updateConfig", @@ -7186,7 +7317,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.update" } }, "/system-config/defaults": { @@ -7218,7 +7350,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" } }, "/system-config/storage-template-options": { @@ -7250,7 +7383,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" } }, "/system-metadata/admin-onboarding": { @@ -7282,7 +7416,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" }, "post": { "operationId": "updateAdminOnboarding", @@ -7315,7 +7450,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.update" } }, "/system-metadata/reverse-geocoding-state": { @@ -7347,7 +7483,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" } }, "/system-metadata/version-check-state": { @@ -7379,7 +7516,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" } }, "/tags": { @@ -7414,7 +7552,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read" }, "post": { "operationId": "createTag", @@ -7454,7 +7593,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create" }, "put": { "operationId": "upsertTags", @@ -7497,7 +7637,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create" } }, "/tags/assets": { @@ -7539,7 +7680,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" } }, "/tags/{id}": { @@ -7574,7 +7716,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.delete" }, "get": { "operationId": "getTagById", @@ -7614,7 +7757,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read" }, "put": { "operationId": "updateTag", @@ -7664,7 +7808,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.update" } }, "/tags/{id}/assets": { @@ -7719,7 +7864,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" }, "put": { "operationId": "tagAssets", @@ -7772,7 +7918,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" } }, "/timeline/bucket": { @@ -7925,7 +8072,8 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read" } }, "/timeline/buckets": { @@ -8071,7 +8219,8 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read" } }, "/trash/empty": { @@ -8103,7 +8252,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/trash/restore": { @@ -8135,7 +8285,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/trash/restore/assets": { @@ -8177,7 +8328,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/users": { @@ -8212,7 +8364,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" } }, "/users/me": { @@ -8244,7 +8397,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" }, "put": { "operationId": "updateMyUser", @@ -8284,7 +8438,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.update" } }, "/users/me/license": { @@ -8309,7 +8464,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.delete" }, "get": { "operationId": "getUserLicense", @@ -8339,7 +8495,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.read" }, "put": { "operationId": "setUserLicense", @@ -8379,7 +8536,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.update" } }, "/users/me/onboarding": { @@ -8404,7 +8562,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.delete" }, "get": { "operationId": "getUserOnboarding", @@ -8434,7 +8593,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.read" }, "put": { "operationId": "setUserOnboarding", @@ -8474,7 +8634,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.update" } }, "/users/me/preferences": { @@ -8506,7 +8667,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.read" }, "put": { "operationId": "updateMyPreferences", @@ -8546,7 +8708,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.update" } }, "/users/profile-image": { @@ -8571,7 +8734,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.delete" }, "post": { "operationId": "createProfileImage", @@ -8612,7 +8776,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.update" } }, "/users/{id}": { @@ -8654,7 +8819,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" } }, "/users/{id}/profile-image": { @@ -8697,7 +8863,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.read" } }, "/view/folder": { diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 69b3cb5ecc..38ff1c373f 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -7,7 +7,7 @@ import { createParamDecorator, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; @@ -27,6 +27,10 @@ export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator = SetMetadata(MetadataKey.AuthRoute, options || {}), ]; + if (options?.permission) { + decorators.push(ApiExtension('x-immich-permission', options.permission)); + } + if ((options as SharedLinkRoute)?.sharedLink) { decorators.push( ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), From ae1d60e259aa0d913b0395eca24bb94234b35f11 Mon Sep 17 00:00:00 2001 From: Alwin Lohrie <46248939+niwla23@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:48:39 +0200 Subject: [PATCH 117/169] feat: find large files utility (#18040) feat: large asset utility Co-authored-by: Jason Rasmussen --- i18n/en.json | 2 + mobile/openapi/README.md | 1 + mobile/openapi/lib/api/search_api.dart | 264 +++++++++++++++ open-api/immich-openapi-specs.json | 317 ++++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 73 ++++ server/src/controllers/search.controller.ts | 8 + server/src/dtos/search.dto.ts | 9 + server/src/queries/search.repository.sql | 21 ++ server/src/repositories/search.repository.ts | 27 +- server/src/services/search.service.ts | 11 + .../specs/services/search.service.spec.ts | 55 +++ .../large-assets/large-asset-data.svelte | 58 ++++ .../utilities-page/utilities-menu.svelte | 11 +- web/src/lib/constants.ts | 1 + web/src/lib/utils/asset-utils.ts | 4 +- .../[[assetId=id]]/+page.svelte | 89 +++++ .../[[photos=photos]]/[[assetId=id]]/+page.ts | 17 + 17 files changed, 964 insertions(+), 4 deletions(-) create mode 100644 server/test/medium/specs/services/search.service.spec.ts create mode 100644 web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte create mode 100644 web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte create mode 100644 web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts diff --git a/i18n/en.json b/i18n/en.json index 39baff3381..5f7f166222 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1154,6 +1154,7 @@ "language_no_results_title": "No languages found", "language_search_hint": "Search languages...", "language_setting_description": "Select your preferred language", + "large_files": "Large Files", "last_seen": "Last seen", "latest_version": "Latest Version", "latitude": "Latitude", @@ -1588,6 +1589,7 @@ "resume": "Resume", "retry_upload": "Retry upload", "review_duplicates": "Review duplicates", + "review_large_files": "Review large files", "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 3181b03a47..058524479c 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -184,6 +184,7 @@ Class | Method | HTTP request | Description *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | *SearchApi* | [**searchAssetStatistics**](doc//SearchApi.md#searchassetstatistics) | **POST** /search/statistics | *SearchApi* | [**searchAssets**](doc//SearchApi.md#searchassets) | **POST** /search/metadata | +*SearchApi* | [**searchLargeAssets**](doc//SearchApi.md#searchlargeassets) | **POST** /search/large-assets | *SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person | *SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places | *SearchApi* | [**searchRandom**](doc//SearchApi.md#searchrandom) | **POST** /search/random | diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 5c7a8de59d..1b58702c40 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -287,6 +287,270 @@ class SearchApi { return null; } + /// Performs an HTTP 'POST /search/large-assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/search/large-assets'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (albumIds != null) { + queryParams.addAll(_queryParams('multi', 'albumIds', albumIds)); + } + if (city != null) { + queryParams.addAll(_queryParams('', 'city', city)); + } + if (country != null) { + queryParams.addAll(_queryParams('', 'country', country)); + } + if (createdAfter != null) { + queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); + } + if (createdBefore != null) { + queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); + } + if (deviceId != null) { + queryParams.addAll(_queryParams('', 'deviceId', deviceId)); + } + if (isEncoded != null) { + queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); + } + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isMotion != null) { + queryParams.addAll(_queryParams('', 'isMotion', isMotion)); + } + if (isNotInAlbum != null) { + queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum)); + } + if (isOffline != null) { + queryParams.addAll(_queryParams('', 'isOffline', isOffline)); + } + if (lensModel != null) { + queryParams.addAll(_queryParams('', 'lensModel', lensModel)); + } + if (libraryId != null) { + queryParams.addAll(_queryParams('', 'libraryId', libraryId)); + } + if (make != null) { + queryParams.addAll(_queryParams('', 'make', make)); + } + if (minFileSize != null) { + queryParams.addAll(_queryParams('', 'minFileSize', minFileSize)); + } + if (model != null) { + queryParams.addAll(_queryParams('', 'model', model)); + } + if (personIds != null) { + queryParams.addAll(_queryParams('multi', 'personIds', personIds)); + } + if (rating != null) { + queryParams.addAll(_queryParams('', 'rating', rating)); + } + if (size != null) { + queryParams.addAll(_queryParams('', 'size', size)); + } + if (state != null) { + queryParams.addAll(_queryParams('', 'state', state)); + } + if (tagIds != null) { + queryParams.addAll(_queryParams('multi', 'tagIds', tagIds)); + } + if (takenAfter != null) { + queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); + } + if (takenBefore != null) { + queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); + } + if (trashedAfter != null) { + queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); + } + if (trashedBefore != null) { + queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (updatedAfter != null) { + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + } + if (updatedBefore != null) { + queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); + } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } + if (withDeleted != null) { + queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); + } + if (withExif != null) { + queryParams.addAll(_queryParams('', 'withExif', withExif)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + final response = await searchLargeAssetsWithHttpInfo( albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. /// Parameters: /// diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 8b305aa8b5..b8089083d1 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -5307,6 +5307,323 @@ "x-immich-permission": "asset.read" } }, + "/search/large-assets": { + "post": { + "operationId": "searchLargeAssets", + "parameters": [ + { + "name": "albumIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "city", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "country", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "createdAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "createdBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "deviceId", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "isEncoded", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isMotion", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isNotInAlbum", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isOffline", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "lensModel", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "libraryId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "nullable": true, + "type": "string" + } + }, + { + "name": "make", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "minFileSize", + "required": false, + "in": "query", + "schema": { + "minimum": 0, + "type": "integer" + } + }, + { + "name": "model", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "personIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "rating", + "required": false, + "in": "query", + "schema": { + "minimum": -1, + "maximum": 5, + "type": "number" + } + }, + { + "name": "size", + "required": false, + "in": "query", + "schema": { + "minimum": 1, + "maximum": 1000, + "type": "number" + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "tagIds", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "takenAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "takenBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetTypeEnum" + } + }, + { + "name": "updatedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "updatedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "visibility", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetVisibility" + } + }, + { + "name": "withDeleted", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withExif", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Search" + ] + } + }, "/search/metadata": { "post": { "operationId": "searchAssets", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 063fc86782..33f0da8a08 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2954,6 +2954,79 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { + albumIds?: string[]; + city?: string | null; + country?: string | null; + createdAfter?: string; + createdBefore?: string; + deviceId?: string; + isEncoded?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + lensModel?: string | null; + libraryId?: string | null; + make?: string; + minFileSize?: number; + model?: string | null; + personIds?: string[]; + rating?: number; + size?: number; + state?: string | null; + tagIds?: string[] | null; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + $type?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + visibility?: AssetVisibility; + withDeleted?: boolean; + withExif?: boolean; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetResponseDto[]; + }>(`/search/large-assets${QS.query(QS.explode({ + albumIds, + city, + country, + createdAfter, + createdBefore, + deviceId, + isEncoded, + isFavorite, + isMotion, + isNotInAlbum, + isOffline, + lensModel, + libraryId, + make, + minFileSize, + model, + personIds, + rating, + size, + state, + tagIds, + takenAfter, + takenBefore, + trashedAfter, + trashedBefore, + "type": $type, + updatedAfter, + updatedBefore, + visibility, + withDeleted, + withExif + }))}`, { + ...opts, + method: "POST" + })); +} export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index fefa916fe7..15f8bc3a5a 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -4,6 +4,7 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, MetadataSearchDto, PlacesResponseDto, RandomSearchDto, @@ -46,6 +47,13 @@ export class SearchController { return this.service.searchRandom(auth, dto); } + @Post('large-assets') + @HttpCode(HttpStatus.OK) + @Authenticated({ permission: Permission.AssetRead }) + searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise { + return this.service.searchLargeAssets(auth, dto); + } + @Post('smart') @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index aef78e51ea..f709ad94ab 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -126,6 +126,15 @@ export class RandomSearchDto extends BaseSearchWithResultsDto { withPeople?: boolean; } +export class LargeAssetSearchDto extends BaseSearchWithResultsDto { + @Optional() + @IsInt() + @Min(0) + @Type(() => Number) + @ApiProperty({ type: 'integer' }) + minFileSize?: number; +} + export class MetadataSearchDto extends RandomSearchDto { @ValidateUUID({ optional: true }) id?: string; diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index ef5363126f..be2245a74e 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -77,6 +77,27 @@ union all limit $15 +-- SearchRepository.searchLargeAssets +select + "asset".*, + to_json("asset_exif") as "exifInfo" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + left join "asset_exif" on "asset"."id" = "asset_exif"."assetId" +where + "asset"."visibility" = $1 + and "asset"."fileCreatedAt" >= $2 + and "asset_exif"."lensModel" = $3 + and "asset"."ownerId" = any ($4::uuid[]) + and "asset"."isFavorite" = $5 + and "asset"."deletedAt" is null + and "asset_exif"."fileSizeInByte" > $6 +order by + "asset_exif"."fileSizeInByte" desc +limit + $7 + -- SearchRepository.searchSmart begin set diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 61e0cc1e29..36ef7a27f1 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -8,7 +8,7 @@ import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; -import { anyUuid, searchAssetBuilder } from 'src/utils/database'; +import { anyUuid, searchAssetBuilder, withExif } from 'src/utils/database'; import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -129,6 +129,8 @@ export type SmartSearchOptions = SearchDateOptions & SearchPeopleOptions & SearchTagOptions; +export type LargeAssetSearchOptions = AssetSearchOptions & { minFileSize?: number }; + export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; numResults: number; @@ -237,6 +239,29 @@ export class SearchRepository { return rows; } + @GenerateSql({ + params: [ + 100, + { + takenAfter: DummyValue.DATE, + lensModel: DummyValue.STRING, + withStacked: true, + isFavorite: true, + userIds: [DummyValue.UUID], + }, + ], + }) + searchLargeAssets(size: number, options: LargeAssetSearchOptions) { + const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection; + return searchAssetBuilder(this.db, options) + .selectAll('asset') + .$call(withExif) + .where('asset_exif.fileSizeInByte', '>', options.minFileSize || 0) + .orderBy('asset_exif.fileSizeInByte', orderDirection) + .limit(size) + .execute(); + } + @GenerateSql({ params: [ { page: 1, size: 200 }, diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 1c75c4a434..b9391fed90 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -4,6 +4,7 @@ import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/ import { AuthDto } from 'src/dtos/auth.dto'; import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, mapPlaces, MetadataSearchDto, PlacesResponseDto, @@ -91,6 +92,16 @@ export class SearchService extends BaseService { return items.map((item) => mapAsset(item, { auth })); } + async searchLargeAssets(auth: AuthDto, dto: LargeAssetSearchDto): Promise { + if (dto.visibility === AssetVisibility.Locked) { + requireElevatedPermission(auth); + } + + const userIds = await this.getUserIdsToSearch(auth); + const items = await this.searchRepository.searchLargeAssets(dto.size || 250, { ...dto, userIds }); + return items.map((item) => mapAsset(item, { auth })); + } + async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise { if (dto.visibility === AssetVisibility.Locked) { requireElevatedPermission(auth); diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts new file mode 100644 index 0000000000..517e6cc277 --- /dev/null +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -0,0 +1,55 @@ +import { Kysely } from 'kysely'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerRepository } from 'src/repositories/partner.repository'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { SearchRepository } from 'src/repositories/search.repository'; +import { DB } from 'src/schema'; +import { SearchService } from 'src/services/search.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SearchService, { + database: db || defaultDatabase, + real: [AccessRepository, DatabaseRepository, SearchRepository, PartnerRepository, PersonRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SearchService.name, () => { + it('should work', () => { + const { sut } = setup(); + expect(sut).toBeDefined(); + }); + + it('should return assets', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + + const assets = []; + const sizes = [12_334, 599, 123_456]; + + for (let i = 0; i < sizes.length; i++) { + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, fileSizeInByte: sizes[i] }); + assets.push(asset); + } + + const auth = factory.auth({ user: { id: user.id } }); + + await expect(sut.searchLargeAssets(auth, {})).resolves.toEqual([ + expect.objectContaining({ id: assets[2].id }), + expect.objectContaining({ id: assets[0].id }), + expect.objectContaining({ id: assets[1].id }), + ]); + }); +}); diff --git a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte new file mode 100644 index 0000000000..71f3dbb5c4 --- /dev/null +++ b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte @@ -0,0 +1,58 @@ + + +
+
+ +
+ +
+
+
{asset.originalFileName}
+ {getAssetResolution(asset)} +
+
+ {getFileSize(asset, 1)} +
+
+
diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index 7deddc6bee..5484ce4ea0 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -1,7 +1,7 @@ @@ -17,4 +17,13 @@ {$t('review_duplicates')} + + + + {$t('review_large_files')} + diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index b354989e17..f2de6d5deb 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -51,6 +51,7 @@ export enum AppRoute { UTILITIES = '/utilities', DUPLICATES = '/utilities/duplicates', + LARGE_FILES = '/utilities/large-files', FOLDERS = '/folders', TAGS = '/tags', diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index d3feff8302..7267520549 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -275,9 +275,9 @@ export function isFlipped(orientation?: string | null) { return value && (isRotated270CW(value) || isRotated90CW(value)); } -export function getFileSize(asset: AssetResponseDto): string { +export function getFileSize(asset: AssetResponseDto, maxPrecision = 4): string { const size = asset.exifInfo?.fileSizeInByte || 0; - return size > 0 ? getByteUnitString(size, undefined, 4) : 'Invalid Data'; + return size > 0 ? getByteUnitString(size, undefined, maxPrecision) : 'Invalid Data'; } export function getAssetResolution(asset: AssetResponseDto): string { diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000..75ac4fab93 --- /dev/null +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,89 @@ + + + +
+ {#if assets && data.assets.length > 0} + {#each assets as asset (asset.id)} + setAsset(asset)} /> + {/each} + {:else} +

+ {$t('no_assets_to_show')} +

+ {/if} +
+
+ +{#if $showAssetViewer} + {#await import('$lib/components/asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} + + 1} + {onNext} + {onPrevious} + {onRandom} + {onAction} + onClose={() => { + assetViewingStore.showAssetViewer(false); + handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); + }} + /> + + {/await} +{/if} diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..6780fdb023 --- /dev/null +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,17 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { searchLargeAssets } from '@immich/sdk'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url); + const assets = await searchLargeAssets({ minFileSize: 0 }); + const $t = await getFormatter(); + + return { + assets, + meta: { + title: $t('large_files'), + }, + }; +}) satisfies PageLoad; From 1804a8fe586b05dc0c5565110d58b37f88110382 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 28 Jul 2025 18:46:34 -0500 Subject: [PATCH 118/169] fix: openapi spec (#20378) --- open-api/immich-openapi-specs.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index b8089083d1..1e9fddf79d 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -5621,7 +5621,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/metadata": { From fbbb6af27a0e03fa38bb41d423f47ae23d88619a Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 20:56:22 -0400 Subject: [PATCH 119/169] chore: update open-api (#20376) From cfae134ecfbe98e598c670a9f5843db05bc92d8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:47:30 +0000 Subject: [PATCH 120/169] fix(deps): update typescript-projects (#20388) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Dietzler --- cli/package-lock.json | 142 +++---- e2e/package-lock.json | 171 ++++----- server/package-lock.json | 807 ++++++++------------------------------- server/package.json | 2 +- web/package-lock.json | 632 +++--------------------------- 5 files changed, 367 insertions(+), 1387 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 3cd4da0480..85e17e6c40 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1365,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1389,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1430,14 +1430,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1452,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1470,9 +1470,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -1487,15 +1487,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1512,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -1526,16 +1526,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1581,16 +1581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1605,13 +1605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2367,9 +2367,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -2383,9 +2383,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3548,15 +3548,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -4139,16 +4139,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 5da041b37b..0d65d3d77f 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -2125,17 +2125,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2149,7 +2149,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -2165,16 +2165,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2190,14 +2190,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2212,14 +2212,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2230,9 +2230,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -2247,15 +2247,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2272,9 +2272,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -2286,16 +2286,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2341,16 +2341,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2365,13 +2365,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3525,9 +3525,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -3541,9 +3541,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3983,15 +3983,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5708,15 +5709,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -6469,35 +6470,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -6817,16 +6818,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package-lock.json b/server/package-lock.json index 5b7ffcdcaf..b5f5889346 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.2", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -415,22 +415,6 @@ "node": ">= 8" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -582,131 +566,6 @@ "node": ">=0.1.90" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@emnapi/runtime": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", @@ -6605,9 +6464,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.0.tgz", - "integrity": "sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", + "integrity": "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6623,16 +6482,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.0", - "@swc/core-darwin-x64": "1.13.0", - "@swc/core-linux-arm-gnueabihf": "1.13.0", - "@swc/core-linux-arm64-gnu": "1.13.0", - "@swc/core-linux-arm64-musl": "1.13.0", - "@swc/core-linux-x64-gnu": "1.13.0", - "@swc/core-linux-x64-musl": "1.13.0", - "@swc/core-win32-arm64-msvc": "1.13.0", - "@swc/core-win32-ia32-msvc": "1.13.0", - "@swc/core-win32-x64-msvc": "1.13.0" + "@swc/core-darwin-arm64": "1.13.2", + "@swc/core-darwin-x64": "1.13.2", + "@swc/core-linux-arm-gnueabihf": "1.13.2", + "@swc/core-linux-arm64-gnu": "1.13.2", + "@swc/core-linux-arm64-musl": "1.13.2", + "@swc/core-linux-x64-gnu": "1.13.2", + "@swc/core-linux-x64-musl": "1.13.2", + "@swc/core-win32-arm64-msvc": "1.13.2", + "@swc/core-win32-ia32-msvc": "1.13.2", + "@swc/core-win32-x64-msvc": "1.13.2" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6644,9 +6503,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.0.tgz", - "integrity": "sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz", + "integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==", "cpu": [ "arm64" ], @@ -6661,9 +6520,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.0.tgz", - "integrity": "sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.2.tgz", + "integrity": "sha512-Lb9EZi7X2XDAVmuUlBm2UvVAgSCbD3qKqDCxSI4jEOddzVOpNCnyZ/xEampdngUIyDDhhJLYU9duC+Mcsv5Y+A==", "cpu": [ "x64" ], @@ -6678,9 +6537,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.0.tgz", - "integrity": "sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.2.tgz", + "integrity": "sha512-9TDe/92ee1x57x+0OqL1huG4BeljVx0nWW4QOOxp8CCK67Rpc/HHl2wciJ0Kl9Dxf2NvpNtkPvqj9+BUmM9WVA==", "cpu": [ "arm" ], @@ -6695,9 +6554,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.0.tgz", - "integrity": "sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz", + "integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==", "cpu": [ "arm64" ], @@ -6712,9 +6571,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.0.tgz", - "integrity": "sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.2.tgz", + "integrity": "sha512-teU27iG1oyWpNh9CzcGQ48ClDRt/RCem7mYO7ehd2FY102UeTws2+OzLESS1TS1tEZipq/5xwx3FzbVgiolCiQ==", "cpu": [ "arm64" ], @@ -6729,9 +6588,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.0.tgz", - "integrity": "sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.2.tgz", + "integrity": "sha512-dRPsyPyqpLD0HMRCRpYALIh4kdOir8pPg4AhNQZLehKowigRd30RcLXGNVZcc31Ua8CiPI4QSgjOIxK+EQe4LQ==", "cpu": [ "x64" ], @@ -6746,9 +6605,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.0.tgz", - "integrity": "sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.2.tgz", + "integrity": "sha512-CCxETW+KkYEQDqz1SYC15YIWYheqFC+PJVOW76Maa/8yu8Biw+HTAcblKf2isrlUtK8RvrQN94v3UXkC2NzCEw==", "cpu": [ "x64" ], @@ -6763,9 +6622,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.0.tgz", - "integrity": "sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.2.tgz", + "integrity": "sha512-Wv/QTA6PjyRLlmKcN6AmSI4jwSMRl0VTLGs57PHTqYRwwfwd7y4s2fIPJVBNbAlXd795dOEP6d/bGSQSyhOX3A==", "cpu": [ "arm64" ], @@ -6780,9 +6639,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.0.tgz", - "integrity": "sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.2.tgz", + "integrity": "sha512-PuCdtNynEkUNbUXX/wsyUC+t4mamIU5y00lT5vJcAvco3/r16Iaxl5UCzhXYaWZSNVZMzPp9qN8NlSL8M5pPxw==", "cpu": [ "ia32" ], @@ -6797,9 +6656,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.0.tgz", - "integrity": "sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.2.tgz", + "integrity": "sha512-qlmMkFZJus8cYuBURx1a3YAG2G7IW44i+FEYV5/32ylKkzGNAr9tDJSA53XNnNXkAB5EXSPsOz7bn5C3JlEtdQ==", "cpu": [ "x64" ], @@ -6820,18 +6679,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@swc/types": { "version": "0.1.23", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", @@ -6843,23 +6690,23 @@ } }, "node_modules/@testcontainers/postgresql": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.2.1.tgz", - "integrity": "sha512-u0XLsjUmAHaUmB9Q1bitBu8uoxRKteDI65S5/zpJ6TeZabx9qB4EENwKqzuqEwOCzzlko9at7ZY4frwRcchgvA==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.4.0.tgz", + "integrity": "sha512-WiKsz3Np5twNZGp2kgatqGaE/KqNR271CPwvIgAvFyN7E581P34glQljM4iLfxdv1XpzVYGWRO6PbQAVDbehBQ==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@testcontainers/redis": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.2.1.tgz", - "integrity": "sha512-Q5j+irNw0BLec3he30s2E0fhE06Zr9ROVutkyKUgcwQoZxEVW3xV69ke2AFCT5teEcIvTKqevObN4UDkq33Qow==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.4.0.tgz", + "integrity": "sha512-w+2VpYt5xAEYbsdhITgwDMif+5Atae+q0ifG/ZrSUZXK528CzqsfnxIgwrZWFnLDCqk1mVNgG4mXdD8VDGd38w==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@tokenizer/inflate": { @@ -7294,9 +7141,9 @@ } }, "node_modules/@types/picomatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", - "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-dLqxmi5VJRC9XTvc/oaTtk+bDb4RRqxLZPZ3jIpYBHEnDXX8lu02w2yWI6NsPPsELuVK298Z2iR8jgoWKRdUVQ==", "dev": true, "license": "MIT" }, @@ -7478,17 +7325,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7502,7 +7349,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7518,16 +7365,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7543,14 +7390,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7412,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7583,9 +7430,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -7600,15 +7447,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7625,9 +7472,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -7639,16 +7486,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7694,16 +7541,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7718,13 +7565,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8884,9 +8731,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.4", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.4.tgz", - "integrity": "sha512-5wSHd0oXs2jS6P+6tay/01Iz0cWRK8iYcscKtpS/GewEq0bJZwbkMZc77GJVOT9SP+UQuXA2y+pQTdCQJel7kQ==", + "version": "5.56.5", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.5.tgz", + "integrity": "sha512-nhcVxoE9Y0YUuNYtvaD+N0Bk2kqcU+rXzJwdQIr8i8qC/fxoghwUYb9a+CidTv24pi1eqstLnBoa8xkR/P7Mdw==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9858,22 +9705,6 @@ "node": ">=4" } }, - "node_modules/cssstyle": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.5.0.tgz", - "integrity": "sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -9881,22 +9712,6 @@ "dev": true, "license": "MIT" }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -9932,15 +9747,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10734,9 +10540,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -10750,9 +10556,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -11491,9 +11297,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -12041,21 +11847,6 @@ "he": "bin/he" } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12502,15 +12293,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -12720,48 +12502,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -14112,15 +13852,6 @@ "set-blocking": "^2.0.0" } }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/nypm": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", @@ -14401,36 +14132,6 @@ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", "license": "MIT" }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -15057,15 +14758,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -15385,9 +15086,9 @@ } }, "node_modules/react-email": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.3.tgz", - "integrity": "sha512-LUKyk9nNVFuTqAyp4yCEQFQjBe+s8nl3VauMWuOhBZ4VhGnimbrnv01U8yD2YwzaHKtytS0U659x5dc/0+xu+Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.4.tgz", + "integrity": "sha512-r5x1nlWUXKZWoIU7l9jx5jkq43RuDUlroH0FRA5MMrCOaLqAfg3vOsAxAadNkG47L0iTeDkkqTKzaV6dTaYf/A==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -16000,15 +15701,6 @@ "node": ">= 18" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -16099,21 +15791,6 @@ "postcss": "^8.3.11" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -17183,35 +16860,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -17260,15 +16937,6 @@ "node": ">=0.10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/synckit": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", @@ -17787,9 +17455,9 @@ } }, "node_modules/testcontainers": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.2.1.tgz", - "integrity": "sha512-KJALGi8ButKDZgzHr0PtJUVNBOSlSFncumZ34MCQTN4VEU9AK4tWTn9gCcAFzG4zBmzzC2aEbHMFUujqkbDvBg==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.4.0.tgz", + "integrity": "sha512-eX5nc/Fi5I0LHqwxw6BuUvWNfdl+M2sKX6fX/47RP89Xs5nU6smd0iD7dpFogxy8/wACjlucLoutJc7b5mtq7w==", "dev": true, "license": "MIT", "dependencies": { @@ -17807,7 +17475,7 @@ "ssh-remote-port-forward": "^1.0.4", "tar-fs": "^3.1.0", "tmp": "^0.2.3", - "undici": "^7.11.0" + "undici": "^7.12.0" } }, "node_modules/testcontainers/node_modules/tmp": { @@ -17929,30 +17597,6 @@ "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -18012,36 +17656,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -18435,16 +18049,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18558,9 +18172,9 @@ } }, "node_modules/undici": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", - "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", "dev": true, "license": "MIT", "engines": { @@ -18974,21 +18588,6 @@ } } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -19012,18 +18611,6 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/webpack": { "version": "5.99.6", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", @@ -19220,49 +18807,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -19401,51 +18945,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index 67945b3dbe..38d4f7b5f7 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.2", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", diff --git a/web/package-lock.json b/web/package-lock.json index ab515030cc..65a016ed02 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1791,50 +1791,50 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.3.tgz", - "integrity": "sha512-6Fjkx2fTUFSmoy6jJxXn+FrwNlMtTi6M8ErEZkQgn7ULenMpIUx6tdFz3l85NM7Br/Rj4CK8IyUo8Le1eI1Fdg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.4.tgz", + "integrity": "sha512-leVQL6gG9wTF+uvCFarHUcr8mzafCZ/GLzauksYQJfiqDVRFSAJNXnTOy7RH9otToluEdjN1hsN1f9HQy+rLYg==", "license": "MIT", "dependencies": { "three": "^0.175.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.3.tgz", - "integrity": "sha512-Gl63vt9ztRHrw2lxDF4KTPsndDbIByg+hUsaMtEAuc304wLUKj6GIapAh71eCyDqvPBhLoTZ5+4RX/TPafnCGg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.4.tgz", + "integrity": "sha512-OdTOKxFunP56FNoPR47mQp7V1WHvV4eiow3qtyJjAgLeU8T2q3kivLuH1kMZN2yTAJaXab+VBXzA/YChiHZ6mQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/video-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/video-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.3.tgz", - "integrity": "sha512-spSBm7OXDisnw5Fx4+JPR510nb+nGFZf2aecPRhP23GZyxKPg82i1PYoDJgRCWKTlvz8Yq2eilKdAtncmHWgSA==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.4.tgz", + "integrity": "sha512-HRBC5zYmpNoo/joKZzXbxn7jwoh3tdtTJFXzHxYPV51ELDclRNmzhmqEaZeVkrFHr4bRF5ow3AOjxiMtu1xQxA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/settings-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/settings-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.3.tgz", - "integrity": "sha512-x/KulP3UxoawDS8MuGrZU3DznrzpaYqQ2BQnxlaOVItewrgCdS8WRG18E7nBo+2lfZGDDwGhRsSp/IRUOwScRg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.4.tgz", + "integrity": "sha512-As1nmlsfnjKBFQOWPVQLH1+dJ+s62MdEb6Jvlm16+3fUVHF4CBWRTJZyBKejLiu4xjbDxrE8v5ZHDLvG6ButiQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.3.tgz", - "integrity": "sha512-npNeknhV3jLJLKbbFzaLRVRROiN2POSdHLnV0R6QNiF7rQURC0Cs7Yuztl2nFJWUBOS3MiO13t5Wyz1wwfKfSQ==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.4.tgz", + "integrity": "sha512-QWbHMVAJHukLbFNn0irND/nEPtmzjbXth1ckBkT1bg8aRilFw50+IIB0Zfdl6X919R2GfGo8P0u+I/Mwxf7yfg==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@pkgjs/parseargs": { @@ -2203,9 +2203,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.25.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.1.tgz", - "integrity": "sha512-8H+fxDEp7Xq6tLFdrGdS5fLu6ONDQQ9DgyjboXpChubuFdfH9QoFX09ypssBpyNkJNZFt9eW3yLmXIc9CesPCA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.2.tgz", + "integrity": "sha512-aKfj82vqEINedoH9Pw4Ip16jj3w8soNq9F3nJqc56kxXW74TcEu/gdTAuLUI+gsl8i+KXfetRqg1F+gG/AZRVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3021,17 +3021,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3045,188 +3045,11 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -3237,33 +3060,17 @@ "node": ">= 4" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3278,150 +3085,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/project-service": { "version": "8.38.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", @@ -5195,9 +4858,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -5619,9 +5282,9 @@ } }, "node_modules/fabric": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.0.tgz", - "integrity": "sha512-+yKumsh1MvJ44Um2eOhb4Q6CyZ6e2XKBV3IfQvzuGKhl2UkRFQtIKPUi6f06m3gd0r5zspgMUl5iwxtT1dmFAQ==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.1.tgz", + "integrity": "sha512-dLxSmIvN4InJf4xOjbl1LFWh8WGOUIYtcuDIGs2IN0Z9lI0zGobfesDauyEhI1+owMLTPCCiEv01rpYXm7g2EQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -8187,15 +7850,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -9773,16 +9436,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9796,174 +9459,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -10928,21 +10423,6 @@ "license": "ISC", "optional": true }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", From 056b262cba30107181d92354c5c99a4bd9660e3f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:48:26 +0200 Subject: [PATCH 121/169] chore(deps): update dependency @types/node to ^22.16.5 (#20385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- e2e/package-lock.json | 6 +++--- e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 2 +- open-api/typescript-sdk/package.json | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 85e17e6c40..3659cb835f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -61,7 +61,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/cli/package.json b/cli/package.json index 6475a91465..2f8147ceef 100644 --- a/cli/package.json +++ b/cli/package.json @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 0d65d3d77f..fe6ba17d4c 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -16,7 +16,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -68,7 +68,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -102,7 +102,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/e2e/package.json b/e2e/package.json index 580cf4982c..228332aaa8 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -26,7 +26,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 61c8807fc5..e906674878 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index fceb764f82..a5582ee60a 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" }, "repository": { diff --git a/server/package-lock.json b/server/package-lock.json index b5f5889346..655b0eaf5b 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -109,7 +109,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", diff --git a/server/package.json b/server/package.json index 38d4f7b5f7..8641e8168c 100644 --- a/server/package.json +++ b/server/package.json @@ -134,7 +134,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From ad6f7f8089f8016bc0291533ba295e3700973ad5 Mon Sep 17 00:00:00 2001 From: Andreas Petersson Date: Tue, 29 Jul 2025 13:02:37 +0200 Subject: [PATCH 122/169] docs: add immich_ml_balancer to community projects (#20399) --- docs/src/components/community-projects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 03a384162b..46e28b3b76 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -100,6 +100,11 @@ const projects: CommunityProjectProps[] = [ description: 'Automatically optimize files uploaded to Immich in order to save storage space', url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', }, + { + title: 'Immich Machine Learning Load Balancer', + description: 'Speed up your machine learning by load balancing your requests to multiple computers', + url: 'https://github.com/apetersson/immich_ml_balancer', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { From 90eac40e0234d3c9406227c9a05e597318c7c2fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:06:52 +0100 Subject: [PATCH 123/169] chore(deps): update terraform cloudflare to v4.52.1 (#20387) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../docs-release/.terraform.lock.hcl | 60 +++++++++---------- .../modules/cloudflare/docs-release/config.tf | 2 +- .../cloudflare/docs/.terraform.lock.hcl | 60 +++++++++---------- deployment/modules/cloudflare/docs/config.tf | 2 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs-release/config.tf b/deployment/modules/cloudflare/docs-release/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs-release/config.tf +++ b/deployment/modules/cloudflare/docs-release/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } diff --git a/deployment/modules/cloudflare/docs/.terraform.lock.hcl b/deployment/modules/cloudflare/docs/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs/config.tf b/deployment/modules/cloudflare/docs/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs/config.tf +++ b/deployment/modules/cloudflare/docs/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } From 59a50b86973a9df51cf30f07841965dc22571b63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:07:14 +0100 Subject: [PATCH 124/169] chore(deps): update github-actions (#20384) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/static_analysis.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/weblate-lock.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f1e68afce..b10bda81f8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 573c908526..0f7b0a7a56 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -129,7 +129,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: sarif_file: results.sarif category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 47a11c8232..baaafccbd1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -668,7 +668,7 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:ec713143dca1a426eba2e03707c319e2ec3cc9d304ef767f777f8e297dee820c env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 084e1a97d6..77bd2b7830 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -38,7 +38,7 @@ jobs: exit 1 fi - name: Find Pull Request - uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 + uses: juliangruber/find-pull-request-action@952b3bb1ddb2dcc0aa3479e98bb1c2d1a922f096 # v1.10.0 id: find-pr with: branch: chore/translations From 2a005629a0069ed0f474cb22f84569855f75c03f Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 29 Jul 2025 12:23:52 +0100 Subject: [PATCH 125/169] chore: bump minimum eslint-config-prettier version due to MAL-2025-6022 (#20400) --- cli/package-lock.json | 2 +- cli/package.json | 2 +- e2e/package-lock.json | 4 ++-- e2e/package.json | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- web/package-lock.json | 4 ++-- web/package.json | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 3659cb835f..2b20af801b 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -33,7 +33,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/cli/package.json b/cli/package.json index 2f8147ceef..a20ae145b4 100644 --- a/cli/package.json +++ b/cli/package.json @@ -27,7 +27,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index fe6ba17d4c..8c1e6e2ba1 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -23,7 +23,7 @@ "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", @@ -74,7 +74,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/e2e/package.json b/e2e/package.json index 228332aaa8..988f1dfd96 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -33,7 +33,7 @@ "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", diff --git a/server/package-lock.json b/server/package-lock.json index 655b0eaf5b..bac0540e38 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -122,7 +122,7 @@ "@vitest/coverage-v8": "^3.0.0", "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/server/package.json b/server/package.json index 8641e8168c..2a5fa5ea8f 100644 --- a/server/package.json +++ b/server/package.json @@ -147,7 +147,7 @@ "@vitest/coverage-v8": "^3.0.0", "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/web/package-lock.json b/web/package-lock.json index 65a016ed02..eb4aae708b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -68,7 +68,7 @@ "autoprefixer": "^10.4.17", "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", @@ -100,7 +100,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/web/package.json b/web/package.json index b9fb5b0400..a8d338d547 100644 --- a/web/package.json +++ b/web/package.json @@ -85,7 +85,7 @@ "autoprefixer": "^10.4.17", "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", From a0fa7318edc58c1e4f988ee79c3c71ec1ec87768 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 29 Jul 2025 13:28:10 +0100 Subject: [PATCH 126/169] fix: handle cleanup of new backups alongside old backups (#20402) --- server/src/services/backup.service.spec.ts | 30 ++++++++++++++-------- server/src/services/backup.service.ts | 9 +++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index e36f699f53..ad60e30425 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -1,3 +1,4 @@ +import { DateTime } from 'luxon'; import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; @@ -90,18 +91,23 @@ describe(BackupService.name, () => { it('should remove failed backup files', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); + //`immich-db-backup-${DateTime.now().toFormat("yyyyLLdd'T'HHmmss")}-v${serverVersion.toString()}-pg${databaseVersion.split(' ')[0]}.sql.gz.tmp`, mocks.storage.readdir.mockResolvedValue([ 'immich-db-backup-123.sql.gz.tmp', - 'immich-db-backup-234.sql.gz', - 'immich-db-backup-345.sql.gz.tmp', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-123.sql.gz.tmp`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-345.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250729T110116-v1.234.5-pg14.5.sql.gz.tmp`, ); }); @@ -118,17 +124,21 @@ describe(BackupService.name, () => { it('should remove old backup files over keepLastAmount and failed backups', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.readdir.mockResolvedValue([ - 'immich-db-backup-1.sql.gz.tmp', - 'immich-db-backup-2.sql.gz', - 'immich-db-backup-3.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + 'immich-db-backup-1753789649000.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1753789649000.sql.gz`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-2.sql.gz`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250727T110116-v1.234.5-pg14.5.sql.gz`, ); }); }); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 9daa2c3aea..0948965f1c 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -53,9 +53,14 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); - const failedBackups = files.filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz\.tmp$/)); + const failedBackups = files.filter((file) => file.match(/immich-db-backup-.*\.sql\.gz\.tmp$/)); const backups = files - .filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz$/)) + .filter((file) => { + const oldBackupStyle = file.match(/immich-db-backup-\d+\.sql\.gz$/); + //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz + const newBackupStyle = file.match(/immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); + return oldBackupStyle || newBackupStyle; + }) .sort() .reverse(); From 3b5e00131bf6dd5a36a2a1120f23ab1947e31ede Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 08:59:10 -0500 Subject: [PATCH 127/169] fix: android widget periodic updates (#20389) * fix: android widget updates * ensure periodic work is queued when we receive an update This will not "reset the clock" on the periodic work since we are using ExistingPeriodicWorkPolicy.UPDATE. This is needed since existing widgets have already been asked to queue their workers. If those periodic workers were overwritten by a widget update request from the app, there is no way to queue them again. onReceive gets run when the app requests a widget update so the periodic workers will get queued again. --- .../immich/widget/ImageDownloadWorker.kt | 2 +- .../app/alextran/immich/widget/MemoryReceiver.kt | 16 +++++++++------- .../app/alextran/immich/widget/RandomReceiver.kt | 16 +++++++++------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt index 3915f291f8..25a7ed99f1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -69,7 +69,7 @@ class ImageDownloadWorker( .build() manager.enqueueUniqueWork( - "$uniqueWorkName-$appWidgetId", + "$uniqueWorkName-$appWidgetId-singleShot", ExistingWorkPolicy.REPLACE, workRequest ) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt index 7721af7d6f..63b32eb6f0 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt @@ -28,19 +28,21 @@ class MemoryReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, MemoryReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, MemoryReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + super.onReceive(context, intent) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt index 39afd76c35..a7662181bc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -28,19 +28,21 @@ class RandomReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, RandomReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, RandomReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + super.onReceive(context, intent) } From e6ec01985285f47bd38fe025f43bd7865306e28f Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:35:04 +0530 Subject: [PATCH 128/169] fix: show missing local assets only in timeline with partner sharing (#20298) fix: show missing local assets in timeline with partner sharing Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../entities/merged_asset.drift | 7 +++--- .../entities/merged_asset.drift.dart | 24 +++++++++---------- .../repositories/timeline.repository.dart | 4 ++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 9778ba723b..579323d641 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -29,7 +29,7 @@ LEFT JOIN WHERE rae.deleted_at IS NULL AND rae.visibility = 0 -- timeline visibility - AND rae.owner_id in ? + AND rae.owner_id IN :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id @@ -57,7 +57,7 @@ SELECT FROM local_asset_entity lae WHERE NOT EXISTS ( - SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids ) AND EXISTS ( SELECT 1 FROM local_album_asset_entity laa @@ -85,7 +85,7 @@ FROM WHERE rae.deleted_at IS NULL AND rae.visibility = 0 -- timeline visibility - AND rae.owner_id in ? + AND rae.owner_id in :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id @@ -103,6 +103,7 @@ FROM local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND rae.owner_id IN :user_ids AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 75f8de2de0..9916ec13bb 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -16,22 +16,22 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset( - List var1, { + i0.Selectable mergedAsset({ + required List userIds, required MergedAsset$limit limit, }) { var $arrayStartIndex = 1; - final expandedvar1 = $expandVar($arrayStartIndex, var1.length); - $arrayStartIndex += var1.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; final generatedlimit = $write( limit(alias(this.localAssetEntity, 'lae')), startIndex: $arrayStartIndex, ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ - for (var $ in var1) i0.Variable($), + for (var $ in userIds) i0.Variable($), ...generatedlimit.introducedVariables, ], readsFrom: { @@ -66,18 +66,18 @@ class MergedAssetDrift extends i1.ModularAccessor { ); } - i0.Selectable mergedBucket( - List var2, { + i0.Selectable mergedBucket({ required int groupBy, + required List userIds, }) { var $arrayStartIndex = 2; - final expandedvar2 = $expandVar($arrayStartIndex, var2.length); - $arrayStartIndex += var2.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND rae.owner_id IN ($expandeduserIds) AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), - for (var $ in var2) i0.Variable($), + for (var $ in userIds) i0.Variable($), ], readsFrom: { remoteAssetEntity, diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index a2c14a363f..dcd10faa64 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -41,7 +41,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } return _db.mergedAssetDrift - .mergedBucket(userIds, groupBy: groupBy.index) + .mergedBucket(userIds: userIds, groupBy: groupBy.index) .map((row) { final date = row.bucketDate.dateFmt(groupBy); return TimeBucket(date: date, assetCount: row.assetCount); @@ -52,7 +52,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: (_) => Limit(count, offset)) + .mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( From 4cae15f28da78b772f469d6066a4f5720d2bb929 Mon Sep 17 00:00:00 2001 From: Andrew Marshall Date: Tue, 29 Jul 2025 16:43:11 -0400 Subject: [PATCH 129/169] feat: support config via systemd credentials (#20406) feat: Support config via Systemd Credentials See https://systemd.io/CREDENTIALS/. This is used as a fallback, so will only be used if the `$*_FILE` var is empty. This could also be used to implicitly use Docker Secrets by settings `CREDENTIALS_DIRECTORY=/run/secrets` rather than setting individual `$_*FILE` environment variables. --- docs/docs/install/environment-variables.md | 8 ++++---- server/bin/start.sh | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 939c42439d..d070e3485a 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -199,12 +199,11 @@ Additional machine learning parameters can be tuned from the admin UI. | `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all | | server | api, microservices | | `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices | -## Docker Secrets +## Secrets -The following variables support the use of [Docker secrets][docker-secrets] for additional security. +The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security. -To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of -the `_FILE` variable should be set to the path of a file containing the variable value. +To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the “regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value. | Regular Variable | Equivalent Docker Secrets '\_FILE' Variable | | :----------------- | :------------------------------------------ | @@ -226,3 +225,4 @@ to use a Docker secret for the password in the Redis container. [docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets [docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ [ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis +[systemd-creds]: https://systemd.io/CREDENTIALS/ diff --git a/server/bin/start.sh b/server/bin/start.sh index 2b4351a6bc..10f897dd8e 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -11,8 +11,12 @@ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" SERVER_HOME=/usr/src/app/server read_file_and_export() { - if [ -n "${!1}" ]; then - content="$(cat "${!1}")" + fname="${!1}" + if [[ -z $fname ]] && [[ -e "$CREDENTIALS_DIRECTORY/$2" ]]; then + fname="${CREDENTIALS_DIRECTORY}/$2" + fi + if [[ -n $fname ]]; then + content="$(< "$fname")" export "$2"="${content}" unset "$1" fi From 58521c9efb1fd142f555d54a7d9496961209a9a7 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 16:58:50 -0400 Subject: [PATCH 130/169] feat: change default media location to /data (#20367) * feat!: change default media location to /data * feat: dynamically detect media location --- .../mobile/container-compose-overrides.yml | 1 - .../server/container-compose-overrides.yml | 1 - docker/docker-compose.dev.yml | 1 - docker/docker-compose.prod.yml | 2 - docker/docker-compose.yml | 2 +- docs/docs/FAQ.mdx | 2 +- docs/docs/administration/server-commands.md | 17 ++- docs/docs/features/libraries.md | 2 +- docs/docs/guides/custom-locations.md | 12 +-- docs/docs/guides/external-library.md | 2 +- docs/docs/install/environment-variables.md | 2 +- docs/src/pages/errors.md | 19 ++++ e2e/docker-compose.yml | 1 - e2e/src/utils.ts | 12 --- server/Dockerfile | 2 +- server/src/commands/media-location.command.ts | 2 +- server/src/constants.ts | 2 - server/src/cores/storage.core.spec.ts | 5 +- server/src/cores/storage.core.ts | 19 +++- server/src/enum.ts | 2 + server/src/repositories/config.repository.ts | 2 + server/src/repositories/storage.repository.ts | 4 + .../src/services/asset-media.service.spec.ts | 16 +-- server/src/services/auth.service.spec.ts | 2 +- server/src/services/download.service.spec.ts | 21 ++-- server/src/services/job.service.spec.ts | 2 +- server/src/services/library.service.spec.ts | 26 ++--- server/src/services/media.service.spec.ts | 17 ++- server/src/services/metadata.service.spec.ts | 6 +- server/src/services/server.service.spec.ts | 12 +-- .../services/storage-template.service.spec.ts | 100 ++++++++---------- server/src/services/storage.service.spec.ts | 48 ++++++--- server/src/services/storage.service.ts | 83 ++++++++++----- server/src/services/user.service.spec.ts | 12 +-- server/test/fixtures/asset.stub.ts | 4 +- .../specs/services/storage.service.spec.ts | 46 ++++++++ .../repositories/storage.repository.mock.ts | 8 +- server/test/small.factory.ts | 2 +- web/src/lib/utils/asset-utils.spec.ts | 6 +- 39 files changed, 316 insertions(+), 209 deletions(-) create mode 100644 server/test/medium/specs/services/storage.service.spec.ts diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index 9fc7486fea..c0655e50e7 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -4,7 +4,6 @@ services: target: dev-container-mobile environment: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ - - IMMICH_MEDIA_LOCATION=/data volumes: !override # bind mount host to /workspaces/immich - ..:/workspaces/immich - cli_node_modules:/workspaces/immich/cli/node_modules diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index 951d763b4b..c4745ad199 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -6,7 +6,6 @@ services: hostname: immich-dev environment: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ - - IMMICH_MEDIA_LOCATION=/data volumes: !override - ..:/workspaces/immich - cli_node_modules:/workspaces/immich/cli/node_modules diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 6db9623b1a..0d918a3dbe 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -36,7 +36,6 @@ services: env_file: - .env environment: - IMMICH_MEDIA_LOCATION: /data IMMICH_REPOSITORY: immich-app/immich IMMICH_REPOSITORY_URL: https://github.com/immich-app/immich IMMICH_SOURCE_REF: local diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index fdeb6ef7a8..838aea4458 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -19,8 +19,6 @@ services: build: context: ../ dockerfile: server/Dockerfile - environment: - - IMMICH_MEDIA_LOCATION=/data volumes: - ${UPLOAD_LOCATION}/photos:/data - /etc/localtime:/etc/localtime:ro diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6af0e0aa2a..2c0895a57a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ services: # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro env_file: - .env diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 9e14d10a0e..e57039a420 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -180,7 +180,7 @@ services: ... volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro + - originals:/usr/src/app/originals ... diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index b275d8fede..a25673abf2 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -94,19 +94,16 @@ Change media location ``` immich-admin change-media-location -? Enter the previous value of IMMICH_MEDIA_LOCATION: /usr/src/app/upload -? Enter the new value of IMMICH_MEDIA_LOCATION: /data +? Enter the previous value of IMMICH_MEDIA_LOCATION: /data +? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data +... + Previous value: /data + Current value: /my-data - Previous value: /usr/src/app/upload - Current value: /data - - Changing database paths from "/usr/src/app/upload/*" to "/data/*" + Changing database paths from "/data/*" to "/my-data/*" ? Do you want to proceed? [Y/n] y Database file paths updated successfully! 🎉 - -You may now set IMMICH_MEDIA_LOCATION=/data and restart! - -(please remember to update applicable volume mounts e.g. ${UPLOAD_LOCATION}:/data) +... ``` diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index caf7ab5e55..f274ca3c70 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -93,7 +93,7 @@ The `immich-server` container will need access to the gallery. Modify your docke ```diff title="docker-compose.yml" immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro + - /home/user/old-pics:/mnt/media/old-pics:ro + - /mnt/media/videos:/mnt/media/videos:ro diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md index ba5caf4b26..af8ca438e7 100644 --- a/docs/docs/guides/custom-locations.md +++ b/docs/docs/guides/custom-locations.md @@ -27,11 +27,11 @@ After defining the locations of these files, we will edit the `docker-compose.ym services: immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload -+ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs -+ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video -+ - ${PROFILE_LOCATION}:/usr/src/app/upload/profile -+ - ${BACKUP_LOCATION}:/usr/src/app/upload/backups + - ${UPLOAD_LOCATION}:/data ++ - ${THUMB_LOCATION}:/data/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video ++ - ${PROFILE_LOCATION}:/data/profile ++ - ${BACKUP_LOCATION}:/data/backups - /etc/localtime:/etc/localtime:ro ``` @@ -44,7 +44,7 @@ docker compose up -d :::note Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device. -For this reason, we mount the HDD or the network storage (NAS) to `/usr/src/app/upload` and then mount the folders we want to access under that folder. +For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder. The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated. diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index 3ad1679423..7921843297 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -12,7 +12,7 @@ If you want Immich to be able to delete the images in the external library or ad ```diff immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /home/user/photos1:/home/user/photos1:ro + - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two ``` diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index d070e3485a..928e0b26e5 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -34,7 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `TZ` | Timezone | \*1 | server | microservices | | `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | | `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload` | server | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/data` | server | api, microservices | | `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | | `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | | `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index e9bf09770c..4a745877dc 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -3,3 +3,22 @@ ## TypeORM Upgrade The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. + +## Inconsistent Media Location + +:::caution +This error is related to the location of media files _inside the container_. Never move files on the host system when you run into this error message. +::: + +Immich automatically tries to detect where your Immich data is located. On start up, it compares the detected media location with the file paths in the database and throws an Inconsistent Media Location error when they do not match. + +To fix this issue, verify that the `IMMICH_MEDIA_LOCATION` environment variable and `UPLOAD_LOCATION` volume mount are in sync with the database paths. + +If you would like to migrate from one media location to another, simply successfully start Immich on `v1.136.0` or later, then do the following steps: + +1. Stop Immich +2. Update `IMMICH_MEDIA_LOCATION` to the new location +3. Update the right-hand side of the `UPLOAD_LOCATION` volume mount to the new location +4. Start up Immich + +After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync. diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index ed57f423bd..ccb21e4587 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -22,7 +22,6 @@ services: - IMMICH_ENV=testing - IMMICH_PORT=2285 - IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true - - IMMICH_MEDIA_LOCATION=/data volumes: - ./test-assets:/test-assets extra_hosts: diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 3fcc4ab552..0f8ceeabfc 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -186,18 +186,6 @@ export const utils = { } }, - resetFilesystem: async () => { - const mediaInternal = '/usr/src/app/upload'; - const dirs = [ - `"${mediaInternal}/thumbs"`, - `"${mediaInternal}/upload"`, - `"${mediaInternal}/library"`, - `"${mediaInternal}/encoded-video"`, - ].join(' '); - - await execPromise(`docker exec -i "immich-e2e-server" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`); - }, - unzip: async (input: string, output: string) => { await execPromise(`unzip -o -d "${output}" "${input}"`); }, diff --git a/server/Dockerfile b/server/Dockerfile index 9110d46f7d..c922c5b291 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -130,7 +130,7 @@ ENV IMMICH_SOURCE_REF=${BUILD_SOURCE_REF} ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT} ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT} -VOLUME /usr/src/app/upload +VOLUME /data EXPOSE 2283 ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] CMD ["start.sh"] diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts index 0935fe202d..0d32749c02 100644 --- a/server/src/commands/media-location.command.ts +++ b/server/src/commands/media-location.command.ts @@ -61,7 +61,7 @@ export class ChangeMediaLocationCommand extends CommandRunner { immich-server: ... volumes: - - \${UPLOAD_LOCATION}:/usr/src/app/upload + - \${UPLOAD_LOCATION}:/data ... )`; diff --git a/server/src/constants.ts b/server/src/constants.ts index 67d50a65f8..3215a58291 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -47,8 +47,6 @@ export const serverVersion = new SemVer(version); export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || '/usr/src/app/upload'; - export const MACHINE_LEARNING_PING_TIMEOUT = Number(process.env.MACHINE_LEARNING_PING_TIMEOUT || 2000); export const MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME = Number( process.env.MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME || 30_000, diff --git a/server/src/cores/storage.core.spec.ts b/server/src/cores/storage.core.spec.ts index 7bb2cdb1be..ed446f9259 100644 --- a/server/src/cores/storage.core.spec.ts +++ b/server/src/cores/storage.core.spec.ts @@ -2,7 +2,6 @@ import { StorageCore } from 'src/cores/storage.core'; import { vitest } from 'vitest'; vitest.mock('src/constants', () => ({ - APP_MEDIA_LOCATION: '/photos', ADDED_IN_PREFIX: 'This property was added in ', DEPRECATED_IN_PREFIX: 'This property was deprecated in ', IWorker: 'IWorker', @@ -10,6 +9,10 @@ vitest.mock('src/constants', () => ({ describe('StorageCore', () => { describe('isImmichPath', () => { + beforeAll(() => { + StorageCore.setMediaLocation('/photos'); + }); + it('should return true for APP_MEDIA_LOCATION path', () => { const immichPath = '/photos'; expect(StorageCore.isImmichPath(immichPath)).toBe(true); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 6576b397e3..06dde4644c 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,6 +1,5 @@ import { randomUUID } from 'node:crypto'; import { dirname, join, resolve } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageAsset } from 'src/database'; import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -32,6 +31,8 @@ export type ThumbnailPathEntity = { id: string; ownerId: string }; let instance: StorageCore | null; +let mediaLocation: string | undefined; + export class StorageCore { private constructor( private assetRepository: AssetRepository, @@ -74,6 +75,18 @@ export class StorageCore { instance = null; } + static getMediaLocation(): string { + if (mediaLocation === undefined) { + throw new Error('Media location is not set.'); + } + + return mediaLocation; + } + + static setMediaLocation(location: string) { + mediaLocation = location; + } + static getFolderLocation(folder: StorageFolder, userId: string) { return join(StorageCore.getBaseFolder(folder), userId); } @@ -83,7 +96,7 @@ export class StorageCore { } static getBaseFolder(folder: StorageFolder) { - return join(APP_MEDIA_LOCATION, folder); + return join(StorageCore.getMediaLocation(), folder); } static getPersonThumbnailPath(person: ThumbnailPathEntity) { @@ -108,7 +121,7 @@ export class StorageCore { static isImmichPath(path: string) { const resolvedPath = resolve(path); - const resolvedAppMediaLocation = resolve(APP_MEDIA_LOCATION); + const resolvedAppMediaLocation = StorageCore.getMediaLocation(); const normalizedPath = resolvedPath.endsWith('/') ? resolvedPath : resolvedPath + '/'; const normalizedAppMediaLocation = resolvedAppMediaLocation.endsWith('/') ? resolvedAppMediaLocation diff --git a/server/src/enum.ts b/server/src/enum.ts index d17b5dc901..666b9fc505 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -475,6 +475,8 @@ export enum DatabaseExtension { export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Detect and configure the media location before jobs are queued which may use it + StorageService = -195, // Other services may need to queue jobs on bootstrap. JobService = -190, // Initialise config after other bootstrap services, stop other services from using config on bootstrap diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index c9e96a1803..d5c279099c 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -97,6 +97,7 @@ export interface EnvData { storage: { ignoreMountCheckErrors: boolean; + mediaLocation?: string; }; workers: ImmichWorker[]; @@ -307,6 +308,7 @@ const getEnv = (): EnvData => { storage: { ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS, + mediaLocation: dto.IMMICH_MEDIA_LOCATION, }, telemetry: { diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 4d89b02a50..7d6b634845 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -162,6 +162,10 @@ export class StorageRepository { } } + existsSync(filepath: string) { + return existsSync(filepath); + } + async checkDiskUsage(folder: string): Promise { const stats = await fs.statfs(folder); return { diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 0585a159ac..91bddeb6e9 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -29,7 +29,7 @@ const uploadFile = { file: { uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/admin/image.jpeg', + originalPath: '/data/library/admin/image.jpeg', originalName: 'image.jpeg', size: 1000, }, @@ -42,7 +42,7 @@ const uploadFile = { uuid: 'random-uuid', mimeType: 'image/jpeg', checksum: Buffer.from('checksum', 'utf8'), - originalPath: `upload/admin/${filename}`, + originalPath: `/data/admin/${filename}`, originalName: filename, size: 1000, }, @@ -294,16 +294,16 @@ describe(AssetMediaService.name, () => { it('should return profile for profile uploads', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.PROFILE_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/profile/admin_id'), + expect.stringContaining('/data/profile/admin_id'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile/admin_id')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile/admin_id')); }); it('should return upload for everything else', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.ASSET_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/upload/admin_id/ra/nd'), + expect.stringContaining('/data/upload/admin_id/ra/nd'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload/admin_id/ra/nd')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload/admin_id/ra/nd')); }); }); @@ -907,14 +907,14 @@ describe(AssetMediaService.name, () => { size: 1000, uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/upload/user-id/ra/nd/random-uuid.jpg', + originalPath: '/data/upload/user-id/ra/nd/random-uuid.jpg', } as unknown as Express.Multer.File; await sut.onUploadError(request, file); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FileDelete, - data: { files: [expect.stringContaining('upload/upload/user-id/ra/nd/random-uuid.jpg')] }, + data: { files: [expect.stringContaining('/data/upload/user-id/ra/nd/random-uuid.jpg')] }, }); }); }); diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 58c2542310..6e19292f71 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -843,7 +843,7 @@ describe(AuthService.name, () => { ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { - profileImagePath: expect.stringContaining(`upload/profile/${user.id}/${fileId}.jpg`), + profileImagePath: expect.stringContaining(`/data/profile/${user.id}/${fileId}.jpg`), profileChangedAt: expect.any(Date), }); expect(mocks.oauth.getProfilePicture).toHaveBeenCalledWith(pictureUrl); diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index 940767ff67..a85fd74c72 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,5 +1,4 @@ import { BadRequestException } from '@nestjs/common'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -49,7 +48,7 @@ describe(DownloadService.name, () => { expect(archiveMock.addFile).toHaveBeenCalledTimes(1); expect(archiveMock.addFile).toHaveBeenNthCalledWith( 1, - expect.stringContaining('upload/library/IMG_123.jpg'), + expect.stringContaining('/data/library/IMG_123.jpg'), 'IMG_123.jpg', ); }); @@ -75,8 +74,8 @@ describe(DownloadService.name, () => { expect(mocks.logger.warn).toHaveBeenCalledTimes(2); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should download an archive', async () => { @@ -98,8 +97,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should handle duplicate file names', async () => { @@ -121,8 +120,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should be deterministic', async () => { @@ -144,8 +143,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should resolve symlinks', async () => { @@ -291,7 +290,7 @@ describe(DownloadService.name, () => { id: 'asset-2', livePhotoVideoId: null, size: 23_456, - originalPath: APP_MEDIA_LOCATION + '/encoded-video/uuid-MP.mp4', + originalPath: '/data/encoded-video/uuid-MP.mp4', }, ]), ); diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index a57db736af..63d5fb2d06 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -11,7 +11,7 @@ describe(JobService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(JobService, {})); + ({ sut, mocks } = newTestService(JobService)); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); }); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 308c80fb37..edd0d46bce 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION, JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; +import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { LibraryService } from 'src/services/library.service'; @@ -24,7 +24,7 @@ describe(LibraryService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(LibraryService, {})); + ({ sut, mocks } = newTestService(LibraryService)); mocks.database.tryLock.mockResolvedValue(true); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); @@ -1171,10 +1171,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(true); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: true, message: undefined, }, @@ -1188,10 +1188,10 @@ describe(LibraryService.name, () => { throw error; }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Path does not exist (ENOENT)', }, @@ -1204,10 +1204,10 @@ describe(LibraryService.name, () => { isDirectory: () => false, } as Stats); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/file'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/file'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/file', + importPath: '/external/user1/file', isValid: false, message: 'Not a directory', }, @@ -1220,10 +1220,10 @@ describe(LibraryService.name, () => { throw new Error('Unknown error'); }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Error: Unknown error', }, @@ -1238,10 +1238,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(false); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Lacking read permission for folder', }, @@ -1264,7 +1264,7 @@ describe(LibraryService.name, () => { }); it('should detect when import path is in immich media folder', async () => { - const importPaths = [APP_MEDIA_LOCATION + '/thumbs', `${process.cwd()}/xyz`, APP_MEDIA_LOCATION + '/library']; + const importPaths = ['/data/thumbs', `${process.cwd()}/xyz`, '/data/library']; const library = factory.library({ importPaths }); mocks.storage.stat.mockResolvedValue({ isDirectory: () => true } as Stats); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 0f4ba769c0..97351af37b 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,6 +1,5 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { Exif } from 'src/database'; import { AssetFileType, @@ -205,19 +204,19 @@ describe(MediaService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.FullSize, oldPath: '/uploads/user-id/fullsize/path.webp', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Preview, oldPath: '/uploads/user-id/thumbs/path.jpg', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-preview.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-preview.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Thumbnail, oldPath: '/uploads/user-id/webp/path.ext', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-thumbnail.webp'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-thumbnail.webp'), }); expect(mocks.move.create).toHaveBeenCalledTimes(3); }); @@ -486,8 +485,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-preview.${format}`; - const thumbnailPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-thumbnail.webp`; + const previewPath = `/data/thumbs/user-id/as/se/asset-id-preview.${format}`; + const thumbnailPath = `/data/thumbs/user-id/as/se/asset-id-thumbnail.webp`; await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -531,8 +530,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-preview.jpeg`); - const thumbnailPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); + const previewPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-preview.jpeg`); + const thumbnailPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -2895,7 +2894,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - APP_MEDIA_LOCATION + '/encoded-video/user-id/as/se/asset-id.mp4', + '/data/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:a copy']), diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index cc0956b9a8..1e304137e4 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -587,7 +587,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -645,7 +645,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -703,7 +703,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 06ddd32601..a96a9925db 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -28,7 +28,7 @@ describe(ServerService.name, () => { diskUseRaw: 300, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as KiB', async () => { @@ -44,7 +44,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as MiB', async () => { @@ -60,7 +60,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as GiB', async () => { @@ -80,7 +80,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as TiB', async () => { @@ -100,7 +100,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as PiB', async () => { @@ -120,7 +120,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); }); diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 882ffcd328..d0d7ea3a3c 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,6 +1,5 @@ import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { AssetPathType, JobStatus } from 'src/enum'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { albumStub } from 'test/fixtures/album.stub'; @@ -111,10 +110,8 @@ describe(StorageTemplateService.name, () => { it('should migrate single moving picture', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newMotionPicturePath = - APP_MEDIA_LOCATION + `/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; - const newStillPicturePath = - APP_MEDIA_LOCATION + `/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; + const newMotionPicturePath = `/data/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; + const newStillPicturePath = `/data/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(stillAsset); mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(motionAsset); @@ -160,7 +157,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -183,7 +180,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -219,7 +216,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -243,9 +240,7 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: - APP_MEDIA_LOCATION + - `/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + newPath: `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -255,9 +250,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset(); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === asset.originalPath)); mocks.move.getByEntity.mockResolvedValue({ @@ -296,9 +290,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset({ fileSizeInByte: 5000 }); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; + const newPath = `/data/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -332,8 +325,7 @@ describe(StorageTemplateService.name, () => { it('should fail move if copying and hash of asset and the new file do not match', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -375,8 +367,8 @@ describe(StorageTemplateService.name, () => { 'should fail to migrate previously failed move from previous new path when old path no longer exists if $reason validation fails', async ({ failedPathChecksum, failedPathSize }) => { mocks.user.get.mockResolvedValue(userStub.user1); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(previousFailedNewPath === path)); mocks.storage.stat.mockResolvedValue({ size: failedPathSize } as Stats); @@ -423,7 +415,7 @@ describe(StorageTemplateService.name, () => { it('should handle an asset with a duplicate destination', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; const newPath2 = newPath.replace('.jpg', '+1.jpg'); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -448,7 +440,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset already matches the template', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -463,7 +455,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset is probably a duplicate', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -480,7 +472,7 @@ describe(StorageTemplateService.name, () => { it('should move an asset', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); mocks.move.create.mockResolvedValue({ @@ -508,7 +500,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, + newPath: `/data/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, }); await sut.handleMigration(); @@ -516,12 +508,12 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, originalPath: expect.stringContaining( - `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + `/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, ), }); }); @@ -529,7 +521,7 @@ describe(StorageTemplateService.name, () => { it('should copy the file if rename fails due to EXDEV (rename across filesystems)', async () => { const asset = assetStub.storageAsset({ originalPath: '/path/to/original.jpg', fileSizeInByte: 5000 }); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -577,7 +569,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + newPath: `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`, }); mocks.storage.stat.mockResolvedValue({ size: 100, @@ -588,14 +580,14 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.copyFile).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.stat).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -619,7 +611,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -630,7 +622,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin({ storageLabel: 'label-1' }); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -639,16 +631,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -656,7 +648,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -665,16 +657,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -682,7 +674,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, originalFileName: 'IMG_7065.JPEG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -691,16 +683,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); @@ -708,7 +700,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', + originalPath: '/data/library/user-id/2022/2022-06-19/IMG_7065.JPG', originalFileName: 'IMG_7065.JPG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -717,16 +709,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); }); diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 0303c11649..3ca9cd7ce2 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -20,6 +20,14 @@ describe(StorageService.name, () => { it('should enable mount folder checking', async () => { mocks.systemMetadata.get.mockResolvedValue(null); mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -33,34 +41,34 @@ describe(StorageService.name, () => { upload: true, }, }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/encoded-video')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/thumbs')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/encoded-video')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/thumbs')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/.immich'), + expect.stringContaining('/data/encoded-video/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/.immich'), + expect.stringContaining('/data/profile/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/.immich'), + expect.stringContaining('/data/thumbs/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/.immich'), + expect.stringContaining('/data/upload/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); @@ -77,6 +85,14 @@ describe(StorageService.name, () => { }, }); mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -91,15 +107,15 @@ describe(StorageService.name, () => { }, }); expect(mocks.storage.mkdirSync).toHaveBeenCalledTimes(2); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledTimes(2); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 6c99194abd..cd8910305a 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,9 +1,16 @@ import { Injectable } from '@nestjs/common'; import { join } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; -import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum'; +import { + BootstrapEventPriority, + DatabaseLock, + JobName, + JobStatus, + QueueName, + StorageFolder, + SystemMetadataKey, +} from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { JobOf, SystemFlags } from 'src/types'; import { ImmichStartupError } from 'src/utils/misc'; @@ -12,9 +19,32 @@ const docsMessage = `Please see https://immich.app/docs/administration/system-in @Injectable() export class StorageService extends BaseService { - @OnEvent({ name: 'AppBootstrap' }) - async onBootstrap() { + private detectMediaLocation(): string { const envData = this.configRepository.getEnv(); + if (envData.storage.mediaLocation) { + return envData.storage.mediaLocation; + } + + const targets: string[] = []; + const candidates = ['/data', '/usr/src/app/upload']; + + for (const candidate of candidates) { + const exists = this.storageRepository.existsSync(candidate); + if (exists) { + targets.push(candidate); + } + } + + if (targets.length === 1) { + return targets[0]; + } + + return '/usr/src/app/upload'; + } + + @OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.StorageService }) + async onBootstrap() { + StorageCore.setMediaLocation(this.detectMediaLocation()); await this.databaseRepository.withLock(DatabaseLock.SystemFileMounts, async () => { const flags = @@ -53,6 +83,7 @@ export class StorageService extends BaseService { this.logger.log('Successfully verified system mount folder checks'); } catch (error) { + const envData = this.configRepository.getEnv(); if (envData.storage.ignoreMountCheckErrors) { this.logger.error(error as Error); this.logger.warn('Ignoring mount folder errors'); @@ -63,30 +94,34 @@ export class StorageService extends BaseService { }); await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { - const current = APP_MEDIA_LOCATION; - const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); - let previous = savedValue?.location || ''; + const current = StorageCore.getMediaLocation(); + const samples = await this.assetRepository.getFileSamples(); + if (samples.length > 0) { + const originalPath = samples[0].originalPath; + const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); + let previous = savedValue?.location || ''; - if (previous !== current) { - this.logger.log(`Media location changed (from=${previous}, to=${current})`); - - const samples = await this.assetRepository.getFileSamples(); - if (samples.length > 0) { - const originalPath = samples[0].originalPath; - if (!previous) { - previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; - } - - if (previous && originalPath.startsWith(previous)) { - this.logger.warn( - `Detected a change to IMMICH_MEDIA_LOCATION, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, - ); - await this.databaseRepository.migrateFilePaths(previous, current); - } + if (!previous) { + previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } - await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + if (previous !== current) { + this.logger.log(`Media location changed (from=${previous}, to=${current})`); + + if (!originalPath.startsWith(previous)) { + throw new Error( + 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', + ); + } + + this.logger.warn( + `Detected a change to media location, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, + ); + await this.databaseRepository.migrateFilePaths(previous, current); + } } + + await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); }); } diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index b4e616974e..bd896ffc24 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -236,23 +236,23 @@ describe(UserService.name, () => { await sut.handleUserDelete({ id: user.id }); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/library/deleted-user'), + expect.stringContaining('/data/library/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/deleted-user'), + expect.stringContaining('/data/upload/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/deleted-user'), + expect.stringContaining('/data/profile/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/deleted-user'), + expect.stringContaining('/data/thumbs/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/deleted-user'), + expect.stringContaining('/data/encoded-video/deleted-user'), options, ); expect(mocks.album.deleteAll).toHaveBeenCalledWith(user.id); @@ -268,7 +268,7 @@ describe(UserService.name, () => { const options = { force: true, recursive: true }; - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('upload/library/admin'), options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('data/library/admin'), options); }); }); diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 76cf71d34d..066996ead5 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -65,7 +65,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_123.jpg', + originalPath: '/data/library/IMG_123.jpg', files: [thumbnailFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -101,7 +101,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_456.jpg', + originalPath: '/data/library/IMG_456.jpg', files: [previewFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, diff --git a/server/test/medium/specs/services/storage.service.spec.ts b/server/test/medium/specs/services/storage.service.spec.ts new file mode 100644 index 0000000000..6c3fc487d4 --- /dev/null +++ b/server/test/medium/specs/services/storage.service.spec.ts @@ -0,0 +1,46 @@ +import { Kysely } from 'kysely'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { StorageRepository } from 'src/repositories/storage.repository'; +import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { DB } from 'src/schema'; +import { StorageService } from 'src/services/storage.service'; +import { newMediumService } from 'test/medium.factory'; +import { mockEnvData } from 'test/repositories/config.repository.mock'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(StorageService, { + database: db || defaultDatabase, + real: [AssetRepository, DatabaseRepository, SystemMetadataRepository], + mock: [StorageRepository, ConfigRepository, LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(StorageService.name, () => { + describe('onBoostrap', () => { + it('should work', async () => { + const { sut, ctx } = setup(); + + const configMock = ctx.getMock(ConfigRepository); + configMock.getEnv.mockReturnValue(mockEnvData({})); + + const storageMock = ctx.getMock(StorageRepository); + storageMock.mkdirSync.mockReturnValue(void 0); + storageMock.existsSync.mockReturnValue(true); + storageMock.createFile.mockResolvedValue(void 0); + storageMock.overwriteFile.mockResolvedValue(void 0); + storageMock.readFile.mockResolvedValue(Buffer.from('test content')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + }); + }); +}); diff --git a/server/test/repositories/storage.repository.mock.ts b/server/test/repositories/storage.repository.mock.ts index dc3fb3e16c..9752a39441 100644 --- a/server/test/repositories/storage.repository.mock.ts +++ b/server/test/repositories/storage.repository.mock.ts @@ -41,10 +41,9 @@ export const makeMockWatcher = return () => Promise.resolve(); }; -export const newStorageRepositoryMock = (reset = true): Mocked> => { - if (reset) { - StorageCore.reset(); - } +export const newStorageRepositoryMock = (): Mocked> => { + StorageCore.reset(); + StorageCore.setMediaLocation('/data'); return { createZipStream: vitest.fn(), @@ -53,6 +52,7 @@ export const newStorageRepositoryMock = (reset = true): Mocked = {}) => ({ livePhotoVideoId: null, localDateTime: newDate(), originalFileName: 'IMG_123.jpg', - originalPath: `upload/12/34/IMG_123.jpg`, + originalPath: `/data/12/34/IMG_123.jpg`, ownerId: newUuid(), sidecarPath: null, stackId: null, diff --git a/web/src/lib/utils/asset-utils.spec.ts b/web/src/lib/utils/asset-utils.spec.ts index b0f76f3bc2..52517975e2 100644 --- a/web/src/lib/utils/asset-utils.spec.ts +++ b/web/src/lib/utils/asset-utils.spec.ts @@ -33,21 +33,21 @@ describe('get asset filename', () => { { asset: { originalFileName: 'filename', - originalPath: 'upload/library/test/2016/2016-08-30/filename.jpg', + originalPath: '/data/library/test/2016/2016-08-30/filename.jpg', }, result: 'filename.jpg', }, { asset: { originalFileName: 'new-filename', - originalPath: 'upload/library/89d14e47-a40d-4cae-a347-a914cdef1f22/2016/2016-08-30/filename.jpg', + originalPath: '/data/library/89d14e47-a40d-4cae-a347-a914cdef1f22/2016/2016-08-30/filename.jpg', }, result: 'new-filename.jpg', }, { asset: { originalFileName: 'new-filename.txt', - originalPath: 'upload/library/test/2016/2016-08-30/filename.txt.jpg', + originalPath: '/data/library/test/2016/2016-08-30/filename.txt.jpg', }, result: 'new-filename.txt.jpg', }, From 290e325c5cd07e30fe484272cc55eda0a3d152f2 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 16:17:33 -0500 Subject: [PATCH 131/169] feat: drift description editor (#20383) * feat: drift description editor * chore: use focus node * chore: code review fixes * chore: move description update to action.service * refactor * refactor --------- Co-authored-by: Alex --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 6 ++ .../asset_viewer/bottom_sheet.widget.dart | 78 +++++++++++++++++++ .../infrastructure/action.provider.dart | 16 ++++ .../repositories/asset_api.repository.dart | 4 + mobile/lib/services/action.service.dart | 8 ++ 6 files changed, 113 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index 5f7f166222..524466f4e3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -988,6 +988,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Add Description...", + "exif_bottom_sheet_description_error": "Error updating description", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATION", "exif_bottom_sheet_people": "PEOPLE", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 1ab62b3442..33735f1709 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -226,6 +226,12 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future updateDescription(String assetId, String description) async { + await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write( + RemoteExifEntityCompanion(description: Value(description)), + ); + } + Future getCount() { return _db.managers.remoteAssetEntity.count(); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 73ec6b456a..f27261a357 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -18,10 +18,12 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; const _kSeparator = ' • '; @@ -147,6 +149,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), + if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), const SheetLocationDetails(), // Details header _SheetTile( @@ -234,3 +237,78 @@ class _SheetTile extends StatelessWidget { ); } } + +class _SheetAssetDescription extends ConsumerStatefulWidget { + final ExifInfo exif; + + const _SheetAssetDescription({required this.exif}); + + @override + ConsumerState<_SheetAssetDescription> createState() => _SheetAssetDescriptionState(); +} + +class _SheetAssetDescriptionState extends ConsumerState<_SheetAssetDescription> { + late TextEditingController _controller; + final _descriptionFocus = FocusNode(); + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.exif.description ?? ''); + } + + Future saveDescription(String? previousDescription) async { + final newDescription = _controller.text.trim(); + + if (newDescription == previousDescription) { + _descriptionFocus.unfocus(); + return; + } + + final editAction = await ref.read(actionProvider.notifier).updateDescription(ActionSource.viewer, newDescription); + + if (!editAction.success) { + _controller.text = previousDescription ?? ''; + + ImmichToast.show( + context: context, + msg: 'exif_bottom_sheet_description_error'.t(context: context), + toastType: ToastType.error, + ); + } + + _descriptionFocus.unfocus(); + } + + @override + Widget build(BuildContext context) { + // Watch the current asset EXIF provider to get updates + final currentExifInfo = ref.watch(currentAssetExifProvider).valueOrNull; + + // Update controller text when EXIF data changes + final currentDescription = currentExifInfo?.description ?? ''; + if (_controller.text != currentDescription && !_descriptionFocus.hasFocus) { + _controller.text = currentDescription; + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + child: TextField( + controller: _controller, + keyboardType: TextInputType.multiline, + focusNode: _descriptionFocus, + maxLines: null, // makes it grow as text is added + decoration: InputDecoration( + hintText: 'exif_bottom_sheet_description'.t(context: context), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + onTapOutside: (_) => saveDescription(currentExifInfo?.description), + ), + ); + } +} diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 9d05a6ecab..69c0532303 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -250,6 +250,22 @@ class ActionNotifier extends Notifier { } } + Future updateDescription(ActionSource source, String description) async { + final ids = _getRemoteIdsForSource(source); + if (ids.length != 1) { + _logger.warning('updateDescription called with multiple assets, expected single asset'); + return ActionResult(count: ids.length, success: false, error: 'Expected single asset for description update'); + } + + try { + final isUpdated = await _service.updateDescription(ids.first, description); + return ActionResult(count: 1, success: isUpdated); + } catch (error, stack) { + _logger.severe('Failed to update description for asset', error, stack); + return ActionResult(count: 1, success: false, error: error.toString()); + } + } + Future stack(String userId, ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 26147292d7..bbb176ffa7 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -93,6 +93,10 @@ class AssetApiRepository extends ApiRepository { // we need to get the MIME of the thumbnail once that gets added to the API return response.originalMimeType; } + + Future updateDescription(String assetId, String description) { + return _api.updateAsset(assetId, UpdateAssetDto(description: description)); + } } extension on StackResponseDto { diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 5a23f16534..f45071e7f7 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -170,6 +170,14 @@ class ActionService { return removedCount; } + Future updateDescription(String assetId, String description) async { + // update remote first, then local to ensure consistency + await _assetApiRepository.updateDescription(assetId, description); + await _remoteAssetRepository.updateDescription(assetId, description); + + return true; + } + Future stack(String userId, List remoteIds) async { final stack = await _assetApiRepository.stack(remoteIds); await _remoteAssetRepository.stack(userId, stack); From 9b65cd4d7b2de166d8835df59f928ab100ae17a5 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 17:28:02 -0400 Subject: [PATCH 132/169] feat!: remove typeorm (#20366) feat: remove typeorm --- docs/src/pages/errors.md | 2 +- server/src/bin/migrations.ts | 27 +--- .../controllers/download.controller.spec.ts | 2 +- .../1645130759468-CreateUserTable.ts | 23 --- .../1645130777674-CreateDeviceInfoTable.ts | 26 ---- .../1645130805273-CreateAssetsTable.ts | 31 ---- .../1645130817965-CreateExifTable.ts | 42 ------ .../1645130870184-CreateSmartInfoTable.ts | 30 ---- .../1646249209023-AddExifTextSearchColumn.ts | 25 ---- ...1646249734844-CreateExifTextSearchIndex.ts | 17 --- .../1646709533213-AddRegionCityToExIf.ts | 29 ---- ...46710459852-AddLocationToExifTextSearch.ts | 37 ----- ...648317474768-AddObjectColumnToSmartInfo.ts | 18 --- ...16111-CreateSharedAlbumAndRelatedTables.ts | 70 --------- ...3525943-UpdateUserTableWithAdminAndName.ts | 36 ----- ...3214255670-UpdateAssetTableWithWebpPath.ts | 17 --- ...583-UpdateAssetTableWithEncodeVideoPath.ts | 17 --- .../1655401127251-RenameSharedAlbums.ts | 19 --- ...56338626260-RenameIsFirstLoggedInColumn.ts | 17 --- ...656888591977-RenameAssetAlbumIdSequence.ts | 17 --- ...6888918620-DropExifTextSearchableColumn.ts | 32 ----- ...1566-MatchMigrationsWithTypeORMEntities.ts | 53 ------- ...470248-AddExifImageNameAsSearchableText.ts | 22 --- .../migrations/1661011331242-AddCaption.ts | 15 -- ...919411-ChangeExifFileSizeInByteToBigInt.ts | 19 --- .../1661881837496-AddAssetChecksum.ts | 17 --- ...UpdateAssetTableWithNewUniqueConstraint.ts | 17 --- ...365521-FixTimestampDataTypeInAssetTable.ts | 21 --- .../1665540663419-CreateSystemConfigTable.ts | 15 -- ...60744-AddingDeletedAtColumnInUserEntity.ts | 13 -- ...-AddLivePhotosRelatedColumnToAssetTable.ts | 16 --- .../1668835311083-UpdateUserTableForOIDC.ts | 16 --- .../src/migrations/1670104716264-OAuthId.ts | 14 -- .../1670257571385-CreateTagsTable.ts | 26 ---- .../1670607437008-TruncateOldConfigItems.ts | 11 -- ...0633210032-AddUserEmailUniqueConstraint.ts | 14 -- .../1672109862870-DropSaltColumn.ts | 14 -- .../migrations/1672502270115-AddAPIKeys.ts | 16 --- .../1673150490490-AddSharedLinkTable.ts | 28 ---- ...907194740-AddMorePermissionToSharedLink.ts | 15 -- ...4263302005-RemoveVideoCodecConfigOption.ts | 12 -- .../1674342044239-CreateUserTokenEntity.ts | 16 --- ...757936889-AlterExifExposureTimeToString.ts | 16 --- .../1674774248319-TruncateAPIKeys.ts | 14 -- ...9-AddSharedLinkUserForeignKeyConstraint.ts | 18 --- ...2-AddUpdatedAtColumnToAlbumsUsersAssets.ts | 17 --- ...909594-AddAlbumUserForeignKeyConstraint.ts | 19 --- .../1675808874445-APIKeyUUIDPrimaryKey.ts | 20 --- .../1675812532822-FixAlbumEntityTypeORM.ts | 82 ----------- .../1676437878377-AppleContentIdentifier.ts | 15 -- .../1676680127415-FixAssetRelations.ts | 30 ---- .../1676721296440-AssetCreatedAtField.ts | 14 -- ...1676848629119-ExifEntityDefinitionFixes.ts | 28 ---- ...8694786-SharedLinkEntityDefinitionFixes.ts | 14 -- ...52143506-SmartInfoEntityDefinitionFixes.ts | 24 ---- .../1677497925328-AddExifTimeZone.ts | 14 -- ...43119-AddIndexForAlbumInSharedLinkTable.ts | 14 -- .../1677613712565-AlbumThumbnailRelation.ts | 60 -------- .../1677971458822-AddCLIPEncodeDataColumn.ts | 13 -- .../1679751316282-UpdateTranscodeOption.ts | 27 ---- .../1679901204458-ClipEmbeddingFloat4.ts | 17 --- .../1680632845740-AddIsArchivedColumn.ts | 14 -- ...680694465853-RemoveRedundantConstraints.ts | 16 --- ...4628393-AddOriginalFileNameToAssetTable.ts | 30 ---- ...1159594469-RemoveImageNameFromEXIFTable.ts | 32 ----- .../1682371561743-FixNullableRelations.ts | 21 --- .../1682371791038-AddDeviceInfoToUserToken.ts | 16 --- .../1682710252424-DropDeviceInfoTable.ts | 26 ---- .../1683808254676-AddPartnersTable.ts | 18 --- .../1684255168091-AddFacialTables.ts | 22 --- .../1684273840676-AddSidecarFile.ts | 14 -- .../1684328185099-RequireChecksumNotNull.ts | 19 --- .../1684410565398-AddStorageLabel.ts | 16 --- ...867360825-AddUserTokenAndAPIKeyCascades.ts | 20 --- .../1685044328272-AddSharedLinkCascade.ts | 16 --- .../1685370430343-UserDatesTimestamptz.ts | 16 --- .../1685731372040-RemoveInvalidCoordinates.ts | 16 --- .../migrations/1686584273471-ImportAsset.ts | 18 --- .../1686762895180-AddThumbhashColumn.ts | 13 -- .../1688241394489-AddDetectFaceResultInfo.ts | 23 --- .../1688392120838-AddLibraryTable.ts | 58 -------- .../1689001889950-DropMimeTypeColumn.ts | 14 -- .../1689281196844-AddHiddenFaces.ts | 14 -- .../src/migrations/1690469489288-Panoramas.ts | 13 -- .../1691209138541-AddAlbumDescription.ts | 13 -- .../1691600216749-UserMemoryPreference.ts | 13 -- .../1692057328660-fixGPSNullIsland.ts | 13 -- .../1692112147855-AddPersonBirthDate.ts | 13 -- .../migrations/1692804658140-AddAuditTable.ts | 16 --- .../1693236627291-RenameMLEnableFlags.ts | 25 ---- .../1693833336881-AddPersonFaceAssetId.ts | 15 -- .../1694204416744-AddAssetDeletedAtColumn.ts | 14 -- .../1694525143117-AddLocalDateTime.ts | 23 --- .../1694638413248-AddDeletedAtToAlbums.ts | 13 -- .../1694750975773-AddExifColorSpace.ts | 18 --- .../1694758412194-UpdateOpusCodecToLibopus.ts | 21 --- .../1695354433573-AddStackParentIdToAssets.ts | 16 --- .../1695660378655-RemoveInvalidCoordinates.ts | 16 --- .../1696888644031-AddOriginalPathIndex.ts | 13 -- .../migrations/1696968880063-AddMoveTable.ts | 14 -- .../migrations/1697272818851-UnassignFace.ts | 23 --- .../1698290827089-AddPasswordToSharedLinks.ts | 14 -- .../migrations/1698693294632-AddActivity.ts | 22 --- .../1699268680508-DisableActivity.ts | 14 -- .../1699322864544-UserNameConsolidation.ts | 21 --- .../migrations/1699345863886-AddJobStatus.ts | 16 --- ...562570201-AdddInTimelineToPartnersTable.ts | 14 -- .../1699727044012-EditFaceAssetForeignKey.ts | 18 --- .../1699889987493-AddAvatarColor.ts | 14 -- .../1700345818045-SystemMetadata.ts | 14 -- .../src/migrations/1700362016675-Geodata.ts | 25 ---- .../migrations/1700713871511-UsePgVectors.ts | 55 ------- .../1700713994428-AddCLIPEmbeddingIndex.ts | 18 --- .../1700714033632-AddFaceEmbeddingIndex.ts | 18 --- .../1700714072055-AddSmartInfoTagsIndex.ts | 13 -- ...14140297-CreateSmartInfoTextSearchIndex.ts | 37 ----- .../1700752078178-AddAssetFaceIndicies.ts | 16 --- .../1701665867595-AddExifCityIndex.ts | 14 -- ...02084989965-AddWebSocketAttachmentTable.ts | 13 -- .../1702257380990-DropNullIslandLatLong.ts | 14 -- ...fyFutureBirthDatesAndAddCheckConstraint.ts | 16 --- ...702942303661-FixRemovedAssetsSharedLink.ts | 16 --- .../1703035138085-AddAutoStackId.ts | 16 --- ...orageTemplateOnForExistingInstallations.ts | 16 --- .../1704382918223-AddQuotaColumnsToUser.ts | 16 --- ...faultOnboardingForExistingInstallations.ts | 17 --- ...43345360-SetAssetFaceNullOnPersonDelete.ts | 24 ---- .../1705094221536-AddMetadataExtractedAt.ts | 19 --- .../1705306747072-AddOriginalFileNameIndex.ts | 13 -- .../1705363967169-CreateAssetStackTable.ts | 83 ----------- .../1707000751533-AddVectorsToSearchPath.ts | 14 -- .../1708059341865-GeodataLocationSearch.ts | 108 -------------- .../1708116312820-GeonamesEnhancement.ts | 18 --- .../1708227417898-AddFileCreatedAtIndex.ts | 12 -- .../1708425975121-RemoveExternalPath.ts | 13 -- ...0004123-RemoveLibraryWatchPollingOption.ts | 12 -- ...140355-AddAssetOriginalPathTrigramIndex.ts | 14 -- ...63765506-AddExtensionToOriginalFileName.ts | 20 --- .../1709825430031-CascadeSharedLinksDelete.ts | 16 --- .../migrations/1709870213078-AddUserStatus.ts | 14 -- .../1710182081326-AscendingOrderAlbum.ts | 14 -- .../1710293990203-AddAssetRelationIndices.ts | 15 -- .../1711257900274-RenameWebpJpegPaths.ts | 51 ------- .../1711637874206-AddMemoryTable.ts | 26 ---- .../1711989989911-AddAssetDuplicateColumns.ts | 14 -- .../1713337511945-AddAlbumUserRole.ts | 14 -- .../1713490844785-RenameSessionsTable.ts | 15 -- .../1714698592332-RemoveIsReadOnly.ts | 14 -- .../1715435221124-MotionAssetExtensionMP4.ts | 11 -- .../1715623169039-RemoveTextSearchColumn.ts | 21 --- .../1715787369686-RemoveSystemConfigTable.ts | 31 ---- .../1715798702876-RemoveLibraryIsVisible.ts | 14 -- .../1715804005643-RemoveLibraryType.ts | 29 ---- .../src/migrations/1715890481637-FixJsonB.ts | 24 ---- .../migrations/1716312279245-UserMetadata.ts | 60 -------- .../1718486162779-AddFaceSearchRelation.ts | 71 ---------- ...1719359859887-FixLivePhotoVideoRelation.ts | 18 --- .../migrations/1720207981949-AddStackOwner.ts | 22 --- .../1720375641148-natural-earth-countries.ts | 14 -- ...721249222549-AddSourceColumnToAssetFace.ts | 16 --- .../migrations/1722753178937-AddExifRating.ts | 14 -- .../1723719333525-AddApiKeyPermissions.ts | 14 -- .../1724080823160-AddThumbnailJobStatus.ts | 17 --- .../1724101822106-AddAssetFilesTable.ts | 34 ----- .../1724790460210-NestedTagTable.ts | 57 -------- .../1725023079109-FixTagUniqueness.ts | 16 --- ...25258039306-UpsertMissingAssetJobStatus.ts | 21 --- ...80-RemoveThumbailAtForMissingThumbnails.ts | 13 -- ...5730782681-RemoveHiddenAssetsFromAlbums.ts | 13 -- .../1726491047923-AddprofileChangedAt.ts | 14 -- .../1726593009549-AddAssetStatus.ts | 16 --- ...7-SeparateQualityForThumbnailAndPreview.ts | 37 ----- .../1727781844613-IsOfflineSetDeletedAt.ts | 16 --- .../1727797340951-AddVersionHistory.ts | 14 -- .../1729793521993-AddAlbumAssetCreatedAt.ts | 13 -- ...1730227312171-RemoveNplFromSystemConfig.ts | 12 -- .../1730989238718-DropSmartInfoTable.ts | 11 -- ...943-NaturalEarthCountriesIdentityColumn.ts | 29 ---- ...39482860-RenameMachineLearningUrlToUrls.ts | 19 --- .../1734574016301-AddTimeBucketIndices.ts | 25 ---- .../1734879118272-AddIsFavoritePerson.ts | 14 -- .../1737672307560-AddUpdatedAtTriggers.ts | 102 ------------- .../migrations/1737845696644-NullableDates.ts | 18 --- .../1738889177573-AddPersonColor.ts | 14 -- ...036-AddDeletedAtColumnToAssetFacesTable.ts | 17 --- .../1739824470990-AddMemoryShowHideDates.ts | 16 --- ...001232576-AddSessionSyncCheckpointTable.ts | 22 --- .../1740064899123-AddUsersAuditTable.ts | 34 ----- .../1740586617223-AddUpdateIdColumns.ts | 134 ------------------ ...740595460866-UsersAuditUuidv7PrimaryKey.ts | 26 ---- .../1740619600996-AddManualSourceType.ts | 27 ---- ...9-UnsetStackedAssetsFromDuplicateStatus.ts | 14 -- .../1740739778549-CreatePartnersAuditTable.ts | 38 ----- .../migrations/1741027685381-ResetMemories.ts | 14 -- .../1741179334403-MoveHistoryUuidEntityId.ts | 26 ---- .../1741191762113-AssetAuditTable.ts | 37 ----- ...328985-FixAssetAndUserCascadeConditions.ts | 50 ------- .../1741281344519-AddExifUpdateId.ts | 25 ---- .../migrations/1743595393000-TableCleanup.ts | 12 -- .../1743611339000-GeodataCleanup.ts | 19 --- ...44662638410-MakeFileMetadataNonNullable.ts | 22 --- .../1744900200559-AddForeignKeyIndexes.ts | 51 ------- .../1744910873956-AddMissingIndex.ts | 13 -- .../src/repositories/database.repository.ts | 57 +------- server/src/services/download.service.spec.ts | 2 +- server/test/utils.ts | 3 +- 206 files changed, 13 insertions(+), 4664 deletions(-) delete mode 100644 server/src/migrations/1645130759468-CreateUserTable.ts delete mode 100644 server/src/migrations/1645130777674-CreateDeviceInfoTable.ts delete mode 100644 server/src/migrations/1645130805273-CreateAssetsTable.ts delete mode 100644 server/src/migrations/1645130817965-CreateExifTable.ts delete mode 100644 server/src/migrations/1645130870184-CreateSmartInfoTable.ts delete mode 100644 server/src/migrations/1646249209023-AddExifTextSearchColumn.ts delete mode 100644 server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts delete mode 100644 server/src/migrations/1646709533213-AddRegionCityToExIf.ts delete mode 100644 server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts delete mode 100644 server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts delete mode 100644 server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts delete mode 100644 server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts delete mode 100644 server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts delete mode 100644 server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts delete mode 100644 server/src/migrations/1655401127251-RenameSharedAlbums.ts delete mode 100644 server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts delete mode 100644 server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts delete mode 100644 server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts delete mode 100644 server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts delete mode 100644 server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts delete mode 100644 server/src/migrations/1661011331242-AddCaption.ts delete mode 100644 server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts delete mode 100644 server/src/migrations/1661881837496-AddAssetChecksum.ts delete mode 100644 server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts delete mode 100644 server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts delete mode 100644 server/src/migrations/1665540663419-CreateSystemConfigTable.ts delete mode 100644 server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts delete mode 100644 server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts delete mode 100644 server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts delete mode 100644 server/src/migrations/1670104716264-OAuthId.ts delete mode 100644 server/src/migrations/1670257571385-CreateTagsTable.ts delete mode 100644 server/src/migrations/1670607437008-TruncateOldConfigItems.ts delete mode 100644 server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts delete mode 100644 server/src/migrations/1672109862870-DropSaltColumn.ts delete mode 100644 server/src/migrations/1672502270115-AddAPIKeys.ts delete mode 100644 server/src/migrations/1673150490490-AddSharedLinkTable.ts delete mode 100644 server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts delete mode 100644 server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts delete mode 100644 server/src/migrations/1674342044239-CreateUserTokenEntity.ts delete mode 100644 server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts delete mode 100644 server/src/migrations/1674774248319-TruncateAPIKeys.ts delete mode 100644 server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts delete mode 100644 server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts delete mode 100644 server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts delete mode 100644 server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts delete mode 100644 server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts delete mode 100644 server/src/migrations/1676437878377-AppleContentIdentifier.ts delete mode 100644 server/src/migrations/1676680127415-FixAssetRelations.ts delete mode 100644 server/src/migrations/1676721296440-AssetCreatedAtField.ts delete mode 100644 server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1677497925328-AddExifTimeZone.ts delete mode 100644 server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts delete mode 100644 server/src/migrations/1677613712565-AlbumThumbnailRelation.ts delete mode 100644 server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts delete mode 100644 server/src/migrations/1679751316282-UpdateTranscodeOption.ts delete mode 100644 server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts delete mode 100644 server/src/migrations/1680632845740-AddIsArchivedColumn.ts delete mode 100644 server/src/migrations/1680694465853-RemoveRedundantConstraints.ts delete mode 100644 server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts delete mode 100644 server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts delete mode 100644 server/src/migrations/1682371561743-FixNullableRelations.ts delete mode 100644 server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts delete mode 100644 server/src/migrations/1682710252424-DropDeviceInfoTable.ts delete mode 100644 server/src/migrations/1683808254676-AddPartnersTable.ts delete mode 100644 server/src/migrations/1684255168091-AddFacialTables.ts delete mode 100644 server/src/migrations/1684273840676-AddSidecarFile.ts delete mode 100644 server/src/migrations/1684328185099-RequireChecksumNotNull.ts delete mode 100644 server/src/migrations/1684410565398-AddStorageLabel.ts delete mode 100644 server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts delete mode 100644 server/src/migrations/1685044328272-AddSharedLinkCascade.ts delete mode 100644 server/src/migrations/1685370430343-UserDatesTimestamptz.ts delete mode 100644 server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts delete mode 100644 server/src/migrations/1686584273471-ImportAsset.ts delete mode 100644 server/src/migrations/1686762895180-AddThumbhashColumn.ts delete mode 100644 server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts delete mode 100644 server/src/migrations/1688392120838-AddLibraryTable.ts delete mode 100644 server/src/migrations/1689001889950-DropMimeTypeColumn.ts delete mode 100644 server/src/migrations/1689281196844-AddHiddenFaces.ts delete mode 100644 server/src/migrations/1690469489288-Panoramas.ts delete mode 100644 server/src/migrations/1691209138541-AddAlbumDescription.ts delete mode 100644 server/src/migrations/1691600216749-UserMemoryPreference.ts delete mode 100644 server/src/migrations/1692057328660-fixGPSNullIsland.ts delete mode 100644 server/src/migrations/1692112147855-AddPersonBirthDate.ts delete mode 100644 server/src/migrations/1692804658140-AddAuditTable.ts delete mode 100644 server/src/migrations/1693236627291-RenameMLEnableFlags.ts delete mode 100644 server/src/migrations/1693833336881-AddPersonFaceAssetId.ts delete mode 100644 server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts delete mode 100644 server/src/migrations/1694525143117-AddLocalDateTime.ts delete mode 100644 server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts delete mode 100644 server/src/migrations/1694750975773-AddExifColorSpace.ts delete mode 100644 server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts delete mode 100644 server/src/migrations/1695354433573-AddStackParentIdToAssets.ts delete mode 100644 server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts delete mode 100644 server/src/migrations/1696888644031-AddOriginalPathIndex.ts delete mode 100644 server/src/migrations/1696968880063-AddMoveTable.ts delete mode 100644 server/src/migrations/1697272818851-UnassignFace.ts delete mode 100644 server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts delete mode 100644 server/src/migrations/1698693294632-AddActivity.ts delete mode 100644 server/src/migrations/1699268680508-DisableActivity.ts delete mode 100644 server/src/migrations/1699322864544-UserNameConsolidation.ts delete mode 100644 server/src/migrations/1699345863886-AddJobStatus.ts delete mode 100644 server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts delete mode 100644 server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts delete mode 100644 server/src/migrations/1699889987493-AddAvatarColor.ts delete mode 100644 server/src/migrations/1700345818045-SystemMetadata.ts delete mode 100644 server/src/migrations/1700362016675-Geodata.ts delete mode 100644 server/src/migrations/1700713871511-UsePgVectors.ts delete mode 100644 server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts delete mode 100644 server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts delete mode 100644 server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts delete mode 100644 server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts delete mode 100644 server/src/migrations/1700752078178-AddAssetFaceIndicies.ts delete mode 100644 server/src/migrations/1701665867595-AddExifCityIndex.ts delete mode 100644 server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts delete mode 100644 server/src/migrations/1702257380990-DropNullIslandLatLong.ts delete mode 100644 server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts delete mode 100644 server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts delete mode 100644 server/src/migrations/1703035138085-AddAutoStackId.ts delete mode 100644 server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts delete mode 100644 server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts delete mode 100644 server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts delete mode 100644 server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts delete mode 100644 server/src/migrations/1705094221536-AddMetadataExtractedAt.ts delete mode 100644 server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts delete mode 100644 server/src/migrations/1705363967169-CreateAssetStackTable.ts delete mode 100644 server/src/migrations/1707000751533-AddVectorsToSearchPath.ts delete mode 100644 server/src/migrations/1708059341865-GeodataLocationSearch.ts delete mode 100644 server/src/migrations/1708116312820-GeonamesEnhancement.ts delete mode 100644 server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts delete mode 100644 server/src/migrations/1708425975121-RemoveExternalPath.ts delete mode 100644 server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts delete mode 100644 server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts delete mode 100644 server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts delete mode 100644 server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts delete mode 100644 server/src/migrations/1709870213078-AddUserStatus.ts delete mode 100644 server/src/migrations/1710182081326-AscendingOrderAlbum.ts delete mode 100644 server/src/migrations/1710293990203-AddAssetRelationIndices.ts delete mode 100644 server/src/migrations/1711257900274-RenameWebpJpegPaths.ts delete mode 100644 server/src/migrations/1711637874206-AddMemoryTable.ts delete mode 100644 server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts delete mode 100644 server/src/migrations/1713337511945-AddAlbumUserRole.ts delete mode 100644 server/src/migrations/1713490844785-RenameSessionsTable.ts delete mode 100644 server/src/migrations/1714698592332-RemoveIsReadOnly.ts delete mode 100644 server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts delete mode 100644 server/src/migrations/1715623169039-RemoveTextSearchColumn.ts delete mode 100644 server/src/migrations/1715787369686-RemoveSystemConfigTable.ts delete mode 100644 server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts delete mode 100644 server/src/migrations/1715804005643-RemoveLibraryType.ts delete mode 100644 server/src/migrations/1715890481637-FixJsonB.ts delete mode 100644 server/src/migrations/1716312279245-UserMetadata.ts delete mode 100644 server/src/migrations/1718486162779-AddFaceSearchRelation.ts delete mode 100644 server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts delete mode 100644 server/src/migrations/1720207981949-AddStackOwner.ts delete mode 100644 server/src/migrations/1720375641148-natural-earth-countries.ts delete mode 100644 server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts delete mode 100644 server/src/migrations/1722753178937-AddExifRating.ts delete mode 100644 server/src/migrations/1723719333525-AddApiKeyPermissions.ts delete mode 100644 server/src/migrations/1724080823160-AddThumbnailJobStatus.ts delete mode 100644 server/src/migrations/1724101822106-AddAssetFilesTable.ts delete mode 100644 server/src/migrations/1724790460210-NestedTagTable.ts delete mode 100644 server/src/migrations/1725023079109-FixTagUniqueness.ts delete mode 100644 server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts delete mode 100644 server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts delete mode 100644 server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts delete mode 100644 server/src/migrations/1726491047923-AddprofileChangedAt.ts delete mode 100644 server/src/migrations/1726593009549-AddAssetStatus.ts delete mode 100644 server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts delete mode 100644 server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts delete mode 100644 server/src/migrations/1727797340951-AddVersionHistory.ts delete mode 100644 server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts delete mode 100644 server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts delete mode 100644 server/src/migrations/1730989238718-DropSmartInfoTable.ts delete mode 100644 server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts delete mode 100644 server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts delete mode 100644 server/src/migrations/1734574016301-AddTimeBucketIndices.ts delete mode 100644 server/src/migrations/1734879118272-AddIsFavoritePerson.ts delete mode 100644 server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts delete mode 100644 server/src/migrations/1737845696644-NullableDates.ts delete mode 100644 server/src/migrations/1738889177573-AddPersonColor.ts delete mode 100644 server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts delete mode 100644 server/src/migrations/1739824470990-AddMemoryShowHideDates.ts delete mode 100644 server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts delete mode 100644 server/src/migrations/1740064899123-AddUsersAuditTable.ts delete mode 100644 server/src/migrations/1740586617223-AddUpdateIdColumns.ts delete mode 100644 server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts delete mode 100644 server/src/migrations/1740619600996-AddManualSourceType.ts delete mode 100644 server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts delete mode 100644 server/src/migrations/1740739778549-CreatePartnersAuditTable.ts delete mode 100644 server/src/migrations/1741027685381-ResetMemories.ts delete mode 100644 server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts delete mode 100644 server/src/migrations/1741191762113-AssetAuditTable.ts delete mode 100644 server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts delete mode 100644 server/src/migrations/1741281344519-AddExifUpdateId.ts delete mode 100644 server/src/migrations/1743595393000-TableCleanup.ts delete mode 100644 server/src/migrations/1743611339000-GeodataCleanup.ts delete mode 100644 server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts delete mode 100644 server/src/migrations/1744900200559-AddForeignKeyIndexes.ts delete mode 100644 server/src/migrations/1744910873956-AddMissingIndex.ts diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index 4a745877dc..c5af7e2575 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,7 +2,7 @@ ## TypeORM Upgrade -The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. +In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully started on this version, shut the system down and try the update again. We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. ## Inconsistent Media Location diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index 3bdfb3bbc6..ebb07af442 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -98,7 +98,7 @@ const create = (path: string, up: string[], down: string[]) => { const folder = dirname(path); const fullPath = join(folder, filename); mkdirSync(folder, { recursive: true }); - writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down })); + writeFileSync(fullPath, asMigration({ up, down })); console.log(`Wrote ${fullPath}`); }; @@ -128,34 +128,11 @@ const compare = async () => { }; type MigrationProps = { - name: string; - timestamp: number; up: string[]; down: string[]; }; -const asMigration = (type: 'kysely' | 'typeorm', options: MigrationProps) => - type === 'typeorm' ? asTypeOrmMigration(options) : asKyselyMigration(options); - -const asTypeOrmMigration = ({ timestamp, name, up, down }: MigrationProps) => { - const upSql = up.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - const downSql = down.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - - return `import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ${name}${timestamp} implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { -${upSql} - } - - public async down(queryRunner: QueryRunner): Promise { -${downSql} - } -} -`; -}; - -const asKyselyMigration = ({ up, down }: MigrationProps) => { +const asMigration = ({ up, down }: MigrationProps) => { const upSql = up.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); const downSql = down.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); diff --git a/server/src/controllers/download.controller.spec.ts b/server/src/controllers/download.controller.spec.ts index 9385c445b5..00d03fc46f 100644 --- a/server/src/controllers/download.controller.spec.ts +++ b/server/src/controllers/download.controller.spec.ts @@ -1,9 +1,9 @@ +import { Readable } from 'node:stream'; import { DownloadController } from 'src/controllers/download.controller'; import { DownloadService } from 'src/services/download.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; describe(DownloadController.name, () => { let ctx: ControllerContext; diff --git a/server/src/migrations/1645130759468-CreateUserTable.ts b/server/src/migrations/1645130759468-CreateUserTable.ts deleted file mode 100644 index 1aedfb67d4..0000000000 --- a/server/src/migrations/1645130759468-CreateUserTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateUserTable1645130759468 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); - await queryRunner.query(` - create table if not exists users - ( - id uuid default uuid_generate_v4() not null - constraint "PK_a3ffb1c0c8416b9fc6f907b7433" - primary key, - email varchar not null, - password varchar not null, - salt varchar not null, - "createdAt" timestamp default now() not null - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table users`); - } -} diff --git a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts b/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts deleted file mode 100644 index bf53d7910b..0000000000 --- a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateDeviceInfoTable1645130777674 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } -} diff --git a/server/src/migrations/1645130805273-CreateAssetsTable.ts b/server/src/migrations/1645130805273-CreateAssetsTable.ts deleted file mode 100644 index 82727e18a5..0000000000 --- a/server/src/migrations/1645130805273-CreateAssetsTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetsTable1645130805273 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists assets - ( - id uuid default uuid_generate_v4() not null - constraint "PK_da96729a8b113377cfb6a62439c" - primary key, - "deviceAssetId" varchar not null, - "userId" varchar not null, - "deviceId" varchar not null, - type varchar not null, - "originalPath" varchar not null, - "resizePath" varchar, - "createdAt" varchar not null, - "modifiedAt" varchar not null, - "isFavorite" boolean default false not null, - "mimeType" varchar, - duration varchar, - constraint "UQ_b599ab0bd9574958acb0b30a90e" - unique ("deviceAssetId", "userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table assets`); - } -} diff --git a/server/src/migrations/1645130817965-CreateExifTable.ts b/server/src/migrations/1645130817965-CreateExifTable.ts deleted file mode 100644 index af46b86507..0000000000 --- a/server/src/migrations/1645130817965-CreateExifTable.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTable1645130817965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists exif - ( - id serial - constraint "PK_28663352d85078ad0046dafafaa" - primary key, - "assetId" uuid not null - constraint "REL_c0117fdbc50b917ef9067740c4" - unique - constraint "FK_c0117fdbc50b917ef9067740c44" - references assets - on delete cascade, - make varchar, - model varchar, - "imageName" varchar, - "exifImageWidth" integer, - "exifImageHeight" integer, - "fileSizeInByte" integer, - orientation varchar, - "dateTimeOriginal" timestamp with time zone, - "modifyDate" timestamp with time zone, - "lensModel" varchar, - "fNumber" double precision, - "focalLength" double precision, - iso integer, - "exposureTime" double precision, - latitude double precision, - longitude double precision - ); - - create unique index if not exists "IDX_c0117fdbc50b917ef9067740c4" on exif ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table exif`); - } -} diff --git a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts b/server/src/migrations/1645130870184-CreateSmartInfoTable.ts deleted file mode 100644 index 9c81f6099a..0000000000 --- a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTable1645130870184 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists smart_info - ( - id serial - constraint "PK_0beace66440e9713f5c40470e46" - primary key, - "assetId" uuid not null - constraint "UQ_5e3753aadd956110bf3ec0244ac" - unique - constraint "FK_5e3753aadd956110bf3ec0244ac" - references assets - on delete cascade, - tags text[] - ); - - create unique index if not exists "IDX_5e3753aadd956110bf3ec0244a" - on smart_info ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table smart_info; - `); - } -} diff --git a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts b/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts deleted file mode 100644 index 071d4bd40d..0000000000 --- a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifTextSearchColumn1646249209023 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') - ) - ) STORED; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - `); - } -} diff --git a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts b/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts deleted file mode 100644 index 664d06c4bc..0000000000 --- a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTextSearchIndex1646249734844 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts b/server/src/migrations/1646709533213-AddRegionCityToExIf.ts deleted file mode 100644 index e2d226cfa4..0000000000 --- a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRegionCityToExIf1646709533213 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN if not exists city varchar; - - ALTER TABLE exif - ADD COLUMN if not exists state varchar; - - ALTER TABLE exif - ADD COLUMN if not exists country varchar; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN city; - - ALTER TABLE exif - DROP COLUMN state; - - ALTER TABLE exif - DROP COLUMN country; - `); - } -} diff --git a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts b/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts deleted file mode 100644 index 9116bf2866..0000000000 --- a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocationToExifTextSearch1646710459852 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts b/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts deleted file mode 100644 index bdf3dff5df..0000000000 --- a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddObjectColumnToSmartInfo1648317474768 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - ADD COLUMN if not exists objects text[]; - - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - DROP COLUMN objects; - `); - } -} diff --git a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts b/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts deleted file mode 100644 index ef633d6f12..0000000000 --- a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSharedAlbumAndRelatedTables1649643216111 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Create shared_albums - await queryRunner.query(` - create table if not exists shared_albums - ( - id uuid default uuid_generate_v4() not null - constraint "PK_7f71c7b5bc7c87b8f94c9a93a00" - primary key, - "ownerId" varchar not null, - "albumName" varchar default 'Untitled Album'::character varying not null, - "createdAt" timestamp with time zone default now() not null, - "albumThumbnailAssetId" varchar - ); - - comment on column shared_albums."albumThumbnailAssetId" is 'Asset ID to be used as thumbnail'; - `); - - // Create user_shared_album - await queryRunner.query(` - create table if not exists user_shared_album - ( - id serial - constraint "PK_b6562316a98845a7b3e9a25cdd0" - primary key, - "albumId" uuid not null - constraint "FK_7b3bf0f5f8da59af30519c25f18" - references shared_albums - on delete cascade, - "sharedUserId" uuid not null - constraint "FK_543c31211653e63e080ba882eb5" - references users, - constraint "PK_unique_user_in_album" - unique ("albumId", "sharedUserId") - ); - `); - - // Create asset_shared_album - await queryRunner.query( - ` - create table if not exists asset_shared_album - ( - id serial - constraint "PK_a34e076afbc601d81938e2c2277" - primary key, - "albumId" uuid not null - constraint "FK_a8b79a84996cef6ba6a3662825d" - references shared_albums - on delete cascade, - "assetId" uuid not null - constraint "FK_64f2e7d68d1d1d8417acc844a4a" - references assets - on delete cascade, - constraint "UQ_a1e2734a1ce361e7a26f6b28288" - unique ("albumId", "assetId") - ); - `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table asset_shared_album; - drop table user_shared_album; - drop table shared_albums; - `); - } -} diff --git a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts b/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts deleted file mode 100644 index af5082ebb2..0000000000 --- a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateUserTableWithAdminAndName1652633525943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - add column if not exists "firstName" varchar default ''; - - alter table users - add column if not exists "lastName" varchar default ''; - - alter table users - add column if not exists "profileImagePath" varchar default ''; - - alter table users - add column if not exists "isAdmin" bool default false; - - alter table users - add column if not exists "isFirstLoggedIn" bool default true; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - drop column "firstName"; - - alter table users - drop column "lastName"; - - alter table users - drop column "isAdmin"; - - `); - } -} diff --git a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts b/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts deleted file mode 100644 index 4de9684f18..0000000000 --- a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithWebpPath1653214255670 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "webpPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "webpPath"; - `); - } -} diff --git a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts b/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts deleted file mode 100644 index 169f7db171..0000000000 --- a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithEncodeVideoPath1654299904583 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "encodedVideoPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "encodedVideoPath"; - `); - } -} diff --git a/server/src/migrations/1655401127251-RenameSharedAlbums.ts b/server/src/migrations/1655401127251-RenameSharedAlbums.ts deleted file mode 100644 index 9bb71fb08c..0000000000 --- a/server/src/migrations/1655401127251-RenameSharedAlbums.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSharedAlbums1655401127251 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE shared_albums RENAME TO albums; - - ALTER TABLE asset_shared_album RENAME TO asset_album; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_album RENAME TO asset_shared_album; - - ALTER TABLE albums RENAME TO shared_albums; - `); - } -} diff --git a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts b/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts deleted file mode 100644 index c4e4d7cd63..0000000000 --- a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameIsFirstLoggedInColumn1656338626260 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "isFirstLoggedIn" to "shouldChangePassword"; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "shouldChangePassword" to "isFirstLoggedIn"; - `); - } -} diff --git a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts b/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts deleted file mode 100644 index 07d5592f53..0000000000 --- a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameAssetAlbumIdSequence1656888591977 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_shared_album_id_seq rename to asset_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_album_id_seq'::regclass);`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_album_id_seq rename to asset_shared_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_shared_album_id_seq'::regclass);`, - ); - } -} diff --git a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts b/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts deleted file mode 100644 index 305b67ee84..0000000000 --- a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropExifTextSearchableColumns1656888918620 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exif_text_searchable_column"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } -} diff --git a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts b/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts deleted file mode 100644 index 00a66d78e9..0000000000 --- a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MatchMigrationsWithTypeORMEntities1656889061566 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts b/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts deleted file mode 100644 index 3b175be3e5..0000000000 --- a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifImageNameAsSearchableText1658860470248 implements MigrationInterface { - name = 'AddExifImageNameAsSearchableText1658860470248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector NOT NULL`); - } -} diff --git a/server/src/migrations/1661011331242-AddCaption.ts b/server/src/migrations/1661011331242-AddCaption.ts deleted file mode 100644 index f6370a7b66..0000000000 --- a/server/src/migrations/1661011331242-AddCaption.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCaption1661011331242 implements MigrationInterface { - name = 'AddCaption1661011331242'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "description" text DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "exif" ADD "fps" double precision`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "fps"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts b/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts deleted file mode 100644 index da614e7f9c..0000000000 --- a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ChangeExifFileSizeInByteToBigInt1661528919411 implements MigrationInterface { - name = 'ChangeExifFileSizeInByteToBigInt1661528919411'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type bigint using "fileSizeInByte"::bigint; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type integer using "fileSizeInByte"::integer; - `); - } -} diff --git a/server/src/migrations/1661881837496-AddAssetChecksum.ts b/server/src/migrations/1661881837496-AddAssetChecksum.ts deleted file mode 100644 index 2901b4f554..0000000000 --- a/server/src/migrations/1661881837496-AddAssetChecksum.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetChecksum1661881837496 implements MigrationInterface { - name = 'AddAssetChecksum1661881837496'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "checksum" bytea`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE 'checksum' IS NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`); - } -} diff --git a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts b/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts deleted file mode 100644 index 15fa467878..0000000000 --- a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithNewUniqueConstraint1661971370662 implements MigrationInterface { - name = 'UpdateAssetTableWithNewUniqueConstraint1661971370662'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("userId", "checksum")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e" UNIQUE ("deviceAssetId", "userId", "deviceId")`, - ); - } -} diff --git a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts b/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts deleted file mode 100644 index a0ce4dc8c6..0000000000 --- a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixTimestampDataTypeInAssetTable1662427365521 implements MigrationInterface { - name = 'FixTimestampDataTypeInAssetTable1662427365521'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts b/server/src/migrations/1665540663419-CreateSystemConfigTable.ts deleted file mode 100644 index 40dd87c644..0000000000 --- a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSystemConfigTable1665540663419 implements MigrationInterface { - name = 'CreateSystemConfigTable1665540663419'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_config"`); - } -} diff --git a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts b/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts deleted file mode 100644 index 1e80fc089a..0000000000 --- a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddingDeletedAtColumnInUserEntity1667762360744 implements MigrationInterface { - name = 'AddingDeletedAtColumnInUserEntity1667762360744'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "deletedAt" TIMESTAMP`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts b/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts deleted file mode 100644 index 62ce314f30..0000000000 --- a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddLivePhotosRelatedColumnToAssetTable1668383120461 implements MigrationInterface { - name = 'AddLivePhotosRelatedColumnToAssetTable1668383120461' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isVisible" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "assets" ADD "livePhotoVideoId" uuid`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "livePhotoVideoId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isVisible"`); - } - -} diff --git a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts b/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts deleted file mode 100644 index 044b79c808..0000000000 --- a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateUserTableForOIDC1668835311083 implements MigrationInterface { - name = 'UpdateUserTableForOIDC1668835311083' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" SET DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" SET DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1670104716264-OAuthId.ts b/server/src/migrations/1670104716264-OAuthId.ts deleted file mode 100644 index 46b99a79d5..0000000000 --- a/server/src/migrations/1670104716264-OAuthId.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class OAuthId1670104716264 implements MigrationInterface { - name = 'OAuthId1670104716264' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "oauthId" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "oauthId"`); - } - -} diff --git a/server/src/migrations/1670257571385-CreateTagsTable.ts b/server/src/migrations/1670257571385-CreateTagsTable.ts deleted file mode 100644 index 75fba9249c..0000000000 --- a/server/src/migrations/1670257571385-CreateTagsTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateTagsTable1670257571385 implements MigrationInterface { - name = 'CreateTagsTable1670257571385' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "tags" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" character varying NOT NULL, "name" character varying NOT NULL, "userId" uuid NOT NULL, "renameTagId" uuid, CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId"), CONSTRAINT "PK_e7dc17249a1148a1970748eda99" PRIMARY KEY ("id")); COMMENT ON COLUMN "tags"."renameTagId" IS 'The new renamed tagId'`); - await queryRunner.query(`CREATE TABLE "tag_asset" ("assetsId" uuid NOT NULL, "tagsId" uuid NOT NULL, CONSTRAINT "PK_ef5346fe522b5fb3bc96454747e" PRIMARY KEY ("assetsId", "tagsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_f8e8a9e893cb5c54907f1b798e" ON "tag_asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4" ON "tag_asset" ("tagsId") `); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`DROP INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4"`); - await queryRunner.query(`DROP INDEX "IDX_f8e8a9e893cb5c54907f1b798e"`); - await queryRunner.query(`DROP TABLE "tag_asset"`); - await queryRunner.query(`DROP TABLE "tags"`); - } - -} diff --git a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts b/server/src/migrations/1670607437008-TruncateOldConfigItems.ts deleted file mode 100644 index 0a82783f89..0000000000 --- a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TruncateOldConfigItems1670607437008 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "system_config"`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts b/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts deleted file mode 100644 index 50a67ae94f..0000000000 --- a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserEmailUniqueConstraint1670633210032 implements MigrationInterface { - name = 'AddUserEmailUniqueConstraint1670633210032' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3"`); - } - -} diff --git a/server/src/migrations/1672109862870-DropSaltColumn.ts b/server/src/migrations/1672109862870-DropSaltColumn.ts deleted file mode 100644 index 91ca5ade11..0000000000 --- a/server/src/migrations/1672109862870-DropSaltColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropSaltColumn1672109862870 implements MigrationInterface { - name = 'DropSaltColumn1672109862870' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "salt"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "salt" character varying NOT NULL DEFAULT ''`); - } - -} diff --git a/server/src/migrations/1672502270115-AddAPIKeys.ts b/server/src/migrations/1672502270115-AddAPIKeys.ts deleted file mode 100644 index e72b3dc2fe..0000000000 --- a/server/src/migrations/1672502270115-AddAPIKeys.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAPIKeys1672502270115 implements MigrationInterface { - name = 'AddAPIKeys1672502270115' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "api_keys" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "key" character varying NOT NULL, "userId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`DROP TABLE "api_keys"`); - } - -} diff --git a/server/src/migrations/1673150490490-AddSharedLinkTable.ts b/server/src/migrations/1673150490490-AddSharedLinkTable.ts deleted file mode 100644 index 8d5bd2f5a5..0000000000 --- a/server/src/migrations/1673150490490-AddSharedLinkTable.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkTable1673150490490 implements MigrationInterface { - name = 'AddSharedLinkTable1673150490490' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "shared_links" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "description" character varying, "userId" character varying NOT NULL, "key" bytea NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "allowUpload" boolean NOT NULL DEFAULT false, "albumId" uuid, CONSTRAINT "UQ_sharedlink_key" UNIQUE ("key"), CONSTRAINT "PK_642e2b0f619e4876e5f90a43465" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_key" ON "shared_links" ("key") `); - await queryRunner.query(`CREATE TABLE "shared_link__asset" ("assetsId" uuid NOT NULL, "sharedLinksId" uuid NOT NULL, CONSTRAINT "PK_9b4f3687f9b31d1e311336b05e3" PRIMARY KEY ("assetsId", "sharedLinksId"))`); - await queryRunner.query(`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId") `); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`DROP INDEX "IDX_c9fab4aa97ffd1b034f3d6581a"`); - await queryRunner.query(`DROP INDEX "IDX_5b7decce6c8d3db9593d6111a6"`); - await queryRunner.query(`DROP TABLE "shared_link__asset"`); - await queryRunner.query(`DROP INDEX "IDX_sharedlink_key"`); - await queryRunner.query(`DROP TABLE "shared_links"`); - } - -} diff --git a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts b/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts deleted file mode 100644 index af0a0280a8..0000000000 --- a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMorePermissionToSharedLink1673907194740 implements MigrationInterface { - name = 'AddMorePermissionToSharedLink1673907194740'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "allowDownload" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD "showExif" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "showExif"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "allowDownload"`); - } -} diff --git a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts b/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts deleted file mode 100644 index 5f64b11559..0000000000 --- a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {MigrationInterface, QueryRunner} from 'typeorm'; - -export class RemoveVideoCodecConfigOption1674263302006 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetVideoCodec'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetAudioCodec'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts b/server/src/migrations/1674342044239-CreateUserTokenEntity.ts deleted file mode 100644 index e289787f91..0000000000 --- a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateUserTokenEntity1674342044239 implements MigrationInterface { - name = 'CreateUserTokenEntity1674342044239' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "user_token" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid, CONSTRAINT "PK_48cb6b5c20faa63157b3c1baf7f" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`DROP TABLE "user_token"`); - } - -} diff --git a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts b/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts deleted file mode 100644 index de21e180b7..0000000000 --- a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AlterExifExposureTimeToString1674757936889 implements MigrationInterface { - name = 'AlterExifExposureTimeToString1674757936889' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" double precision`); - } - -} diff --git a/server/src/migrations/1674774248319-TruncateAPIKeys.ts b/server/src/migrations/1674774248319-TruncateAPIKeys.ts deleted file mode 100644 index efbb5c41af..0000000000 --- a/server/src/migrations/1674774248319-TruncateAPIKeys.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class TruncateAPIKeys1674774248319 implements MigrationInterface { - name = 'TruncateAPIKeys1674774248319' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "api_keys"`); - } - - public async down(): Promise { - //noop - } - -} diff --git a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts b/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts deleted file mode 100644 index 9119c57065..0000000000 --- a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSharedLinkUserForeignKeyConstraint1674939383309 implements MigrationInterface { - name = 'AddSharedLinkUserForeignKeyConstraint1674939383309'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE uuid using "userId"::uuid`); - await queryRunner.query( - `ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts b/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts deleted file mode 100644 index 90c00b38e9..0000000000 --- a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtColumnToAlbumsUsersAssets1675667878312 implements MigrationInterface { - name = 'AddUpdatedAtColumnToAlbumsUsersAssets1675667878312'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "users" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updatedAt"`); - } -} diff --git a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts b/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts deleted file mode 100644 index 0898accfb7..0000000000 --- a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumUserForeignKeyConstraint1675701909594 implements MigrationInterface { - name = 'AddAlbumUserForeignKeyConstraint1675701909594'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "albums" WHERE "ownerId"::uuid NOT IN (SELECT id FROM "users") `) - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE uuid using "ownerId"::uuid`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts b/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts deleted file mode 100644 index 368a9ca9c5..0000000000 --- a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class APIKeyUUIDPrimaryKey1675808874445 implements MigrationInterface { - name = 'APIKeyUUIDPrimaryKey1675808874445' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - -} diff --git a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts b/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts deleted file mode 100644 index 6f48ac736d..0000000000 --- a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface { - name = 'FixAlbumEntityTypeORM1675812532822' - - public async up(queryRunner: QueryRunner): Promise { - - await queryRunner.query(`ALTER TABLE "asset_album" RENAME TO "albums_assets_assets"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "PK_a34e076afbc601d81938e2c2277"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "assetId" TO "assetsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId")`); - await queryRunner.query(`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "user_shared_album" RENAME TO "albums_shared_users_users"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_543c31211653e63e080ba882eb5"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_unique_user_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "sharedUserId" TO "usersId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId")`); - await queryRunner.query(`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId") `); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`) - await queryRunner.query(`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME TO "asset_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME TO "user_shared_album"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_e590fa396c6898fcd4a50e40927"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`); - await queryRunner.query(`DROP INDEX "IDX_427c350ad49bd3935a50baab73"`); - await queryRunner.query(`DROP INDEX "IDX_f48513bf9bccefd6ff3ad30bd0"`); - await queryRunner.query(`DROP INDEX "IDX_e590fa396c6898fcd4a50e4092"`); - await queryRunner.query(`DROP INDEX "IDX_4bd1303d199f4e72ccdf998c62"`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_323f8dcbe85373722886940f143" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "usersId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_323f8dcbe85373722886940f143"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "albumsId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "sharedUserId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "albumId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_unique_user_in_album" UNIQUE ("albumId", "sharedUserId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_543c31211653e63e080ba882eb5" FOREIGN KEY ("sharedUserId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "albumsId" TO "albumId"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "assetsId" TO "assetId"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_a34e076afbc601d81938e2c2277" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1676437878377-AppleContentIdentifier.ts b/server/src/migrations/1676437878377-AppleContentIdentifier.ts deleted file mode 100644 index 8d11139878..0000000000 --- a/server/src/migrations/1676437878377-AppleContentIdentifier.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AppleContentIdentifier1676437878377 implements MigrationInterface { - name = 'AppleContentIdentifier1676437878377'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "livePhotoCID" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_live_photo_cid"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`); - } -} diff --git a/server/src/migrations/1676680127415-FixAssetRelations.ts b/server/src/migrations/1676680127415-FixAssetRelations.ts deleted file mode 100644 index 439e86a78a..0000000000 --- a/server/src/migrations/1676680127415-FixAssetRelations.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAssetRelations1676680127415 implements MigrationInterface { - name = 'FixAssetRelations1676680127415' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "modifiedAt" TO "fileModifiedAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "createdAt" TO "fileCreatedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "userId" TO "ownerId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "ownerId" TYPE uuid USING "ownerId"::uuid;`); - - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileCreatedAt" TO "createdAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileModifiedAt" TO "modifiedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "ownerId" TO "userId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "userId" TYPE varchar`); - } - -} diff --git a/server/src/migrations/1676721296440-AssetCreatedAtField.ts b/server/src/migrations/1676721296440-AssetCreatedAtField.ts deleted file mode 100644 index 304c7b2190..0000000000 --- a/server/src/migrations/1676721296440-AssetCreatedAtField.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetCreatedAtField1676721296440 implements MigrationInterface { - name = 'AssetCreatedAtField1676721296440' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "createdAt"`); - } - -} diff --git a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts b/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts deleted file mode 100644 index 947559ed2d..0000000000 --- a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ExifEntityDefinitionFixes1676848629119 implements MigrationInterface { - name = 'ExifEntityDefinitionFixes1676848629119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`); - - await queryRunner.query(`DROP INDEX "IDX_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_c0117fdbc50b917ef9067740c44" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" DROP NOT NULL`); - - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "exif" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_28663352d85078ad0046dafafaa" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c0117fdbc50b917ef9067740c4" ON "exif" ("assetId") `); - } - -} diff --git a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts b/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts deleted file mode 100644 index d48f543fef..0000000000 --- a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SharedLinkEntityDefinitionFixes1676848694786 implements MigrationInterface { - name = 'SharedLinkEntityDefinitionFixes1676848694786' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" SET DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts b/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts deleted file mode 100644 index e089619c6d..0000000000 --- a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SmartInfoEntityDefinitionFixes1676852143506 implements MigrationInterface { - name = 'SmartInfoEntityDefinitionFixes1676852143506' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_5e3753aadd956110bf3ec0244a"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_0beace66440e9713f5c40470e46" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5e3753aadd956110bf3ec0244a" ON "smart_info" ("assetId") `); - } - -} diff --git a/server/src/migrations/1677497925328-AddExifTimeZone.ts b/server/src/migrations/1677497925328-AddExifTimeZone.ts deleted file mode 100644 index 33f958336e..0000000000 --- a/server/src/migrations/1677497925328-AddExifTimeZone.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifTimeZone1677497925328 implements MigrationInterface { - name = 'AddExifTimeZone1677497925328' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "timeZone" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "timeZone"`); - } - -} diff --git a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts b/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts deleted file mode 100644 index 986b5ebd20..0000000000 --- a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIndexForAlbumInSharedLinkTable1677535643119 implements MigrationInterface { - name = 'AddIndexForAlbumInSharedLinkTable1677535643119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_sharedlink_albumId"`); - } - -} diff --git a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts b/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts deleted file mode 100644 index 71f022dcf6..0000000000 --- a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AlbumThumbnailRelation1677613712565 implements MigrationInterface { - name = 'AlbumThumbnailRelation1677613712565'; - - public async up(queryRunner: QueryRunner): Promise { - // Make sure all albums have a valid albumThumbnailAssetId UUID or NULL. - await queryRunner.query(` - UPDATE "albums" - SET - "albumThumbnailAssetId" = ( - SELECT - "albums_assets2"."assetsId" - FROM - "assets" "assets", - "albums_assets_assets" "albums_assets2" - WHERE - "albums_assets2"."assetsId" = "assets"."id" - AND "albums_assets2"."albumsId" = "albums"."id" - ORDER BY - "assets"."fileCreatedAt" DESC - LIMIT 1 - ), - "updatedAt" = CURRENT_TIMESTAMP - WHERE - "albums"."albumThumbnailAssetId" IS NULL - AND EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE "albums"."id" = "albums_assets"."albumsId" - ) - OR "albums"."albumThumbnailAssetId" IS NOT NULL - AND NOT EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE - "albums"."id" = "albums_assets"."albumsId" - AND "albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"::varchar - ) - `); - - await queryRunner.query(` - ALTER TABLE "albums" - ALTER COLUMN "albumThumbnailAssetId" - TYPE uuid USING "albumThumbnailAssetId"::uuid - `); - - await queryRunner.query(` - ALTER TABLE "albums" ADD CONSTRAINT "FK_05895aa505a670300d4816debce" FOREIGN KEY ("albumThumbnailAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_05895aa505a670300d4816debce"`); - - await queryRunner.query(` - ALTER TABLE "albums" ALTER COLUMN "albumThumbnailAssetId" TYPE varchar USING "albumThumbnailAssetId"::varchar - `); - } -} diff --git a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts b/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts deleted file mode 100644 index 82f8176b0d..0000000000 --- a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEncodeDataColumn1677971458822 implements MigrationInterface { - name = 'AddCLIPEncodeDataColumn1677971458822'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD "clipEmbedding" numeric(20,19) array`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "clipEmbedding"`); - } -} diff --git a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts b/server/src/migrations/1679751316282-UpdateTranscodeOption.ts deleted file mode 100644 index 989622e831..0000000000 --- a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateTranscodeOption1679751316282 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcode', - value = '"all"' - WHERE - key = 'ffmpeg.transcodeAll' AND value = 'true' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcodeAll', - value = 'true' - WHERE - key = 'ffmpeg.transcode' AND value = '"all"' - `); - - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.transcode'`); - } -} diff --git a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts b/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts deleted file mode 100644 index 3afa8c6d10..0000000000 --- a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ClipEmbeddingFloat41679901204458 implements MigrationInterface { - name = 'ClipEmbeddingFloat41679901204458'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE real array USING "clipEmbedding"::real array`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE numeric(20,19) array USING "clipEmbedding"::numeric(20,19) array`, - ); - } -} diff --git a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts b/server/src/migrations/1680632845740-AddIsArchivedColumn.ts deleted file mode 100644 index 325e37f489..0000000000 --- a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsArchivedColumn1680632845740 implements MigrationInterface { - name = 'AddIsArchivedColumn1680632845740' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isArchived" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isArchived"`); - } - -} diff --git a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts b/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts deleted file mode 100644 index 1c3b051b02..0000000000 --- a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveRedundantConstraints1680694465853 implements MigrationInterface { - name = 'RemoveRedundantConstraints1680694465853' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "REL_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac" UNIQUE ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "REL_c0117fdbc50b917ef9067740c4" UNIQUE ("assetId")`); - } - -} diff --git a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts b/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts deleted file mode 100644 index 7c547108b5..0000000000 --- a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameToAssetTable1681144628393 implements MigrationInterface { - name = 'AddOriginalFileNameToAssetTable1681144628393'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "originalFileName" character varying`); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = ( - select e."imageName" - from exif e - where e."assetId" = a.id - ) - `); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = a.id - where a."originalFileName" IS NULL or a."originalFileName" = '' - `); - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "originalFileName" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "originalFileName"`); - } -} diff --git a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts b/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts deleted file mode 100644 index e188ea3506..0000000000 --- a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveImageNameFromEXIFTable1681159594469 implements MigrationInterface { - name = 'RemoveImageNameFromEXIFTable1681159594469'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN IF EXISTS "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "imageName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD "imageName" character varying`); - } -} diff --git a/server/src/migrations/1682371561743-FixNullableRelations.ts b/server/src/migrations/1682371561743-FixNullableRelations.ts deleted file mode 100644 index 42c34f9399..0000000000 --- a/server/src/migrations/1682371561743-FixNullableRelations.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixNullableRelations1682371561743 implements MigrationInterface { - name = 'FixNullableRelations1682371561743'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" DROP NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts b/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts deleted file mode 100644 index bb60e452ef..0000000000 --- a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddDeviceInfoToUserToken1682371791038 implements MigrationInterface { - name = 'AddDeviceInfoToUserToken1682371791038' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceType" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceOS" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceOS"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceType"`); - } - -} diff --git a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts b/server/src/migrations/1682710252424-DropDeviceInfoTable.ts deleted file mode 100644 index 9a07676351..0000000000 --- a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropDeviceInfoTable1682710252424 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } -} diff --git a/server/src/migrations/1683808254676-AddPartnersTable.ts b/server/src/migrations/1683808254676-AddPartnersTable.ts deleted file mode 100644 index 64afb0b76c..0000000000 --- a/server/src/migrations/1683808254676-AddPartnersTable.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPartnersTable1683808254676 implements MigrationInterface { - name = 'AddPartnersTable1683808254676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners" ("sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_f1cc8f73d16b367f426261a8736" PRIMARY KEY ("sharedById", "sharedWithId"))`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_7e077a8b70b3530138610ff5e04" FOREIGN KEY ("sharedById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3" FOREIGN KEY ("sharedWithId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3"`); - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_7e077a8b70b3530138610ff5e04"`); - await queryRunner.query(`DROP TABLE "partners"`); - } - -} diff --git a/server/src/migrations/1684255168091-AddFacialTables.ts b/server/src/migrations/1684255168091-AddFacialTables.ts deleted file mode 100644 index 1f2426bb0c..0000000000 --- a/server/src/migrations/1684255168091-AddFacialTables.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFacialTables1684255168091 implements MigrationInterface { - name = 'AddFacialTables1684255168091' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "person" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ownerId" uuid NOT NULL, "name" character varying NOT NULL DEFAULT '', "thumbnailPath" character varying NOT NULL DEFAULT '', CONSTRAINT "PK_5fdaf670315c4b7e70cce85daa3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "asset_faces" ("assetId" uuid NOT NULL, "personId" uuid NOT NULL, "embedding" real array, CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId"))`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_5527cc99f530a547093f9e577b6" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c"`); - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_5527cc99f530a547093f9e577b6"`); - await queryRunner.query(`DROP TABLE "asset_faces"`); - await queryRunner.query(`DROP TABLE "person"`); - } - -} diff --git a/server/src/migrations/1684273840676-AddSidecarFile.ts b/server/src/migrations/1684273840676-AddSidecarFile.ts deleted file mode 100644 index 46c4b5d375..0000000000 --- a/server/src/migrations/1684273840676-AddSidecarFile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSidecarFile1684273840676 implements MigrationInterface { - name = 'AddSidecarFile1684273840676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "sidecarPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "sidecarPath"`); - } - -} diff --git a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts b/server/src/migrations/1684328185099-RequireChecksumNotNull.ts deleted file mode 100644 index e691fff2b1..0000000000 --- a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RequireChecksumNotNull1684328185099 implements MigrationInterface { - name = 'removeNotNullFromChecksumIndex1684328185099'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_8d3efe36c0755849395e6ea866"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`, - ); - } -} diff --git a/server/src/migrations/1684410565398-AddStorageLabel.ts b/server/src/migrations/1684410565398-AddStorageLabel.ts deleted file mode 100644 index 6c6ea9702f..0000000000 --- a/server/src/migrations/1684410565398-AddStorageLabel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStorageLabel1684410565398 implements MigrationInterface { - name = 'AddStorageLabel1684410565398' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "storageLabel" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a" UNIQUE ("storageLabel")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "storageLabel"`); - } - -} diff --git a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts b/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts deleted file mode 100644 index 273c6b830f..0000000000 --- a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserTokenAndAPIKeyCascades1684867360825 implements MigrationInterface { - name = 'AddUserTokenAndAPIKeyCascades1684867360825' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts b/server/src/migrations/1685044328272-AddSharedLinkCascade.ts deleted file mode 100644 index 3aefd3989e..0000000000 --- a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkCascade1685044328272 implements MigrationInterface { - name = 'AddSharedLinkCascade1685044328272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts b/server/src/migrations/1685370430343-UserDatesTimestamptz.ts deleted file mode 100644 index 0a70e012d8..0000000000 --- a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UserDatesTimestamptz1685370430343 implements MigrationInterface { - name = 'UserDatesTimestamptz1685370430343' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP`); - } - -} diff --git a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts b/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts deleted file mode 100644 index 9a9b00a366..0000000000 --- a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1685731372040 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1685731372040'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1686584273471-ImportAsset.ts b/server/src/migrations/1686584273471-ImportAsset.ts deleted file mode 100644 index d9f5819a8d..0000000000 --- a/server/src/migrations/1686584273471-ImportAsset.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ImportAsset1686584273471 implements MigrationInterface { - name = 'ImportAsset1686584273471' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`); - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - -} diff --git a/server/src/migrations/1686762895180-AddThumbhashColumn.ts b/server/src/migrations/1686762895180-AddThumbhashColumn.ts deleted file mode 100644 index 4ad73163db..0000000000 --- a/server/src/migrations/1686762895180-AddThumbhashColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbhashColumn1685546571785 implements MigrationInterface { - name = 'AddThumbhashColumn1686762895180'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbhash" bytea NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbhash"`); - } -} diff --git a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts b/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts deleted file mode 100644 index b026685bfb..0000000000 --- a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDetectFaceResultInfo1688241394489 implements MigrationInterface { - name = 'AddDetectFaceResultInfo1688241394489'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageWidth" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageHeight" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX2" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY2" integer NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageHeight"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageWidth"`); - } -} diff --git a/server/src/migrations/1688392120838-AddLibraryTable.ts b/server/src/migrations/1688392120838-AddLibraryTable.ts deleted file mode 100644 index 4d394adaf1..0000000000 --- a/server/src/migrations/1688392120838-AddLibraryTable.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLibraries1688392120838 implements MigrationInterface { - name = 'AddLibraryTable1688392120838'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `CREATE TABLE "libraries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "importPaths" text array NOT NULL, "exclusionPatterns" text array NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "refreshedAt" TIMESTAMP WITH TIME ZONE, "isVisible" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_505fedfcad00a09b3734b4223de" PRIMARY KEY ("id"))`, - ); - await queryRunner.query(`ALTER TABLE "assets" ADD "isOffline" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD "libraryId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" ADD "isExternal" boolean NOT NULL DEFAULT false`); - - await queryRunner.query( - `CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" on "assets" ("ownerId", "libraryId", checksum)`, - ); - await queryRunner.query( - `ALTER TABLE "libraries" ADD CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - - // Create default library for each user and assign all assets to it - const users = await queryRunner.query(`SELECT id FROM "users"`); - const userIds: string[] = users.map((user: any) => user.id); - - for (const userId of userIds) { - await queryRunner.query( - `INSERT INTO "libraries" ("name", "ownerId", "type", "importPaths", "exclusionPatterns") VALUES ('Default Library', '${userId}', 'UPLOAD', '{}', '{}')`, - ); - - await queryRunner.query( - `UPDATE "assets" SET "libraryId" = (SELECT id FROM "libraries" WHERE "ownerId" = '${userId}' LIMIT 1) WHERE "ownerId" = '${userId}'`, - ); - } - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_owner_library_originalpath"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`, - ); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isOffline"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isExternal"`); - await queryRunner.query(`DROP TABLE "libraries"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("ownerId", "checksum")`); - } -} diff --git a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts b/server/src/migrations/1689001889950-DropMimeTypeColumn.ts deleted file mode 100644 index 45559313a1..0000000000 --- a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropMimeTypeColumn1689001889950 implements MigrationInterface { - name = 'DropMimeTypeColumn1689001889950' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "mimeType"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "mimeType" character varying`); - } - -} diff --git a/server/src/migrations/1689281196844-AddHiddenFaces.ts b/server/src/migrations/1689281196844-AddHiddenFaces.ts deleted file mode 100644 index 234b77dd34..0000000000 --- a/server/src/migrations/1689281196844-AddHiddenFaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Infra1689281196844 implements MigrationInterface { - name = 'Infra1689281196844' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isHidden" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isHidden"`); - } - -} diff --git a/server/src/migrations/1690469489288-Panoramas.ts b/server/src/migrations/1690469489288-Panoramas.ts deleted file mode 100644 index ee0934b43a..0000000000 --- a/server/src/migrations/1690469489288-Panoramas.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class Panoramas1690217088596 implements MigrationInterface { - name = 'Panoramas1690217088596'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "projectionType" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "projectionType"`); - } -} diff --git a/server/src/migrations/1691209138541-AddAlbumDescription.ts b/server/src/migrations/1691209138541-AddAlbumDescription.ts deleted file mode 100644 index f4167598af..0000000000 --- a/server/src/migrations/1691209138541-AddAlbumDescription.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumDescription1691209138541 implements MigrationInterface { - name = 'AddAlbumDescription1691209138541'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "description" text NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1691600216749-UserMemoryPreference.ts b/server/src/migrations/1691600216749-UserMemoryPreference.ts deleted file mode 100644 index 7238749080..0000000000 --- a/server/src/migrations/1691600216749-UserMemoryPreference.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMemoryPreference1691600216749 implements MigrationInterface { - name = 'UserMemoryPreference1691600216749'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - } -} diff --git a/server/src/migrations/1692057328660-fixGPSNullIsland.ts b/server/src/migrations/1692057328660-fixGPSNullIsland.ts deleted file mode 100644 index 74dc40a474..0000000000 --- a/server/src/migrations/1692057328660-fixGPSNullIsland.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class FixGPSNullIsland1692057328660 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;`); - } - - public async down(): Promise { - // Setting lat,lon to 0 not necessary - } - -} diff --git a/server/src/migrations/1692112147855-AddPersonBirthDate.ts b/server/src/migrations/1692112147855-AddPersonBirthDate.ts deleted file mode 100644 index db2ba35dad..0000000000 --- a/server/src/migrations/1692112147855-AddPersonBirthDate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonBirthDate1692112147855 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "birthDate" date`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "birthDate"`); - } - -} diff --git a/server/src/migrations/1692804658140-AddAuditTable.ts b/server/src/migrations/1692804658140-AddAuditTable.ts deleted file mode 100644 index d398051a79..0000000000 --- a/server/src/migrations/1692804658140-AddAuditTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAuditTable1692804658140 implements MigrationInterface { - name = 'AddAuditTable1692804658140' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "audit" ("id" SERIAL NOT NULL, "entityType" character varying NOT NULL, "entityId" uuid NOT NULL, "action" character varying NOT NULL, "ownerId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_1d3d120ddaf7bc9b1ed68ed463a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_ownerId_createdAt" ON "audit" ("ownerId", "createdAt") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_ownerId_createdAt"`); - await queryRunner.query(`DROP TABLE "audit"`); - } - -} diff --git a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts b/server/src/migrations/1693236627291-RenameMLEnableFlags.ts deleted file mode 100644 index 096356f6e0..0000000000 --- a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class RenameMLEnableFlags1693236627291 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classificationEnabled' THEN 'ffmpeg.classification.enabled' - WHEN key = 'ffmpeg.clipEnabled' THEN 'ffmpeg.clip.enabled' - WHEN key = 'ffmpeg.facialRecognitionEnabled' THEN 'ffmpeg.facialRecognition.enabled' - ELSE key - END - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classification.enabled' THEN 'ffmpeg.classificationEnabled' - WHEN key = 'ffmpeg.clip.enabled' THEN 'ffmpeg.clipEnabled' - WHEN key = 'ffmpeg.facialRecognition.enabled' THEN 'ffmpeg.facialRecognitionEnabled' - ELSE key - END - `); - } -} diff --git a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts b/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts deleted file mode 100644 index 2e3c914eec..0000000000 --- a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonFaceAssetId1693833336881 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "faceAssetId" uuid`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "faceAssetId"`); - } - -} diff --git a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts b/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts deleted file mode 100644 index 3b213b9f04..0000000000 --- a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetDeletedAtColumn1694204416744 implements MigrationInterface { - name = 'AddAssetDeletedAtColumn1694204416744' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "deletedAt"`); - } - -} diff --git a/server/src/migrations/1694525143117-AddLocalDateTime.ts b/server/src/migrations/1694525143117-AddLocalDateTime.ts deleted file mode 100644 index dd24c07c0b..0000000000 --- a/server/src/migrations/1694525143117-AddLocalDateTime.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocalDateTime1694525143117 implements MigrationInterface { - name = 'AddLocalDateTime1694525143117'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "localDateTime" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "assets" SET "localDateTime" = "fileCreatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "localDateTime"`); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } -} diff --git a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts b/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts deleted file mode 100644 index 64a34c3e88..0000000000 --- a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtToAlbums1694638413248 implements MigrationInterface { - name = 'AddDeletedAtToAlbums1694638413248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1694750975773-AddExifColorSpace.ts b/server/src/migrations/1694750975773-AddExifColorSpace.ts deleted file mode 100644 index d6b3a6b2b0..0000000000 --- a/server/src/migrations/1694750975773-AddExifColorSpace.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifColorSpace1694750975773 implements MigrationInterface { - name = 'AddExifColorSpace1694750975773' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "profileDescription" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "colorspace" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "bitsPerSample" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "bitsPerSample"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "colorspace"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "profileDescription"`); - } - -} diff --git a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts b/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts deleted file mode 100644 index 715b46550a..0000000000 --- a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateOpusCodecToLibopus1694758412194 implements MigrationInterface { - name = 'UpdateOpusCodecToLibopus1694758412194' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"libopus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"opus"' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"opus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"libopus"' - `); - } -} diff --git a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts b/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts deleted file mode 100644 index d5150d3a81..0000000000 --- a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackParentIdToAssets1695354433573 implements MigrationInterface { - name = 'AddStackParentIdToAssets1695354433573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "stackParentId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - -} diff --git a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts b/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts deleted file mode 100644 index 20b179ceb6..0000000000 --- a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1695660378655 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1695660378655'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts b/server/src/migrations/1696888644031-AddOriginalPathIndex.ts deleted file mode 100644 index 78e1c92ecb..0000000000 --- a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalPathIndex1696888644031 implements MigrationInterface { - name = 'AddOriginalPathIndex1696888644031'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - } -} diff --git a/server/src/migrations/1696968880063-AddMoveTable.ts b/server/src/migrations/1696968880063-AddMoveTable.ts deleted file mode 100644 index 7ba140d05b..0000000000 --- a/server/src/migrations/1696968880063-AddMoveTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMoveTable1696968880063 implements MigrationInterface { - name = 'AddMoveTable1696968880063' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" character varying NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL, CONSTRAINT "UQ_newPath" UNIQUE ("newPath"), CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType"), CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "move_history"`); - } - -} diff --git a/server/src/migrations/1697272818851-UnassignFace.ts b/server/src/migrations/1697272818851-UnassignFace.ts deleted file mode 100644 index 49eebf4cc1..0000000000 --- a/server/src/migrations/1697272818851-UnassignFace.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnassignFace1697272818851 implements MigrationInterface { - name = 'UnassignFace1697272818851'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_bf339a24070dac7e71304ec530a"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD COLUMN "id" UUID DEFAULT uuid_generate_v4() NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts b/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts deleted file mode 100644 index b6906e3d05..0000000000 --- a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPasswordToSharedLinks1698290827089 implements MigrationInterface { - name = 'AddPasswordToSharedLinks1698290827089' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "password" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "password"`); - } - -} diff --git a/server/src/migrations/1698693294632-AddActivity.ts b/server/src/migrations/1698693294632-AddActivity.ts deleted file mode 100644 index 5556ef2b20..0000000000 --- a/server/src/migrations/1698693294632-AddActivity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddActivity1698693294632 implements MigrationInterface { - name = 'AddActivity1698693294632' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "activity" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "assetId" uuid, "comment" text, "isLiked" boolean NOT NULL DEFAULT false, CONSTRAINT "CHK_2ab1e70f113f450eb40c1e3ec8" CHECK (("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)), CONSTRAINT "PK_24625a1d6b1b089c8ae206fe467" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_activity_like" ON "activity" ("assetId", "userId", "albumId") WHERE ("isLiked" = true)`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_8091ea76b12338cb4428d33d782" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_1af8519996fbfb3684b58df280b" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`); - await queryRunner.query(`DROP INDEX "IDX_activity_like"`); - await queryRunner.query(`DROP TABLE "activity"`); - } - -} diff --git a/server/src/migrations/1699268680508-DisableActivity.ts b/server/src/migrations/1699268680508-DisableActivity.ts deleted file mode 100644 index d860244f6d..0000000000 --- a/server/src/migrations/1699268680508-DisableActivity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DisableActivity1699268680508 implements MigrationInterface { - name = 'DisableActivity1699268680508' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "isActivityEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "isActivityEnabled"`); - } - -} diff --git a/server/src/migrations/1699322864544-UserNameConsolidation.ts b/server/src/migrations/1699322864544-UserNameConsolidation.ts deleted file mode 100644 index 431a2a92fc..0000000000 --- a/server/src/migrations/1699322864544-UserNameConsolidation.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsername1699322864544 implements MigrationInterface { - name = 'AddUsername1699322864544' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "name" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "name" = CONCAT(COALESCE("firstName", ''), ' ', COALESCE("lastName", ''))`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "firstName"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "users" ADD "lastName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ADD "firstName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "lastName" = COALESCE("email", '')`); - await queryRunner.query(`UPDATE "users" SET "firstName" = COALESCE("email", '')`); - } - -} diff --git a/server/src/migrations/1699345863886-AddJobStatus.ts b/server/src/migrations/1699345863886-AddJobStatus.ts deleted file mode 100644 index c7df6387c0..0000000000 --- a/server/src/migrations/1699345863886-AddJobStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddJobStatus1699345863886 implements MigrationInterface { - name = 'AddJobStatus1699345863886' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_job_status" ("assetId" uuid NOT NULL, "facesRecognizedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_420bec36fc02813bddf5c8b73d4" PRIMARY KEY ("assetId"))`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4"`); - await queryRunner.query(`DROP TABLE "asset_job_status"`); - } - -} diff --git a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts b/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts deleted file mode 100644 index 59c2f229eb..0000000000 --- a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AdddInTimelineToPartnersTable1699562570201 implements MigrationInterface { - name = 'AdddInTimelineToPartnersTable1699562570201' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" ADD "inTimeline" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "inTimeline"`); - } - -} diff --git a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts b/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts deleted file mode 100644 index fdff7ea062..0000000000 --- a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EditFaceAssetForeignKey1699727044012 implements MigrationInterface { - name = 'EditFaceAssetForeignKey1699727044012' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = asset_faces."id" FROM asset_faces WHERE person."faceAssetId" = asset_faces."assetId" AND person."id" = asset_faces."personId"`) - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "asset_faces"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = assets."id" FROM assets, asset_faces WHERE person."faceAssetId" = asset_faces."id" AND asset_faces."assetId" = assets."id"`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1699889987493-AddAvatarColor.ts b/server/src/migrations/1699889987493-AddAvatarColor.ts deleted file mode 100644 index b075a5d2af..0000000000 --- a/server/src/migrations/1699889987493-AddAvatarColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAvatarColor1699889987493 implements MigrationInterface { - name = 'AddAvatarColor1699889987493' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - } - -} diff --git a/server/src/migrations/1700345818045-SystemMetadata.ts b/server/src/migrations/1700345818045-SystemMetadata.ts deleted file mode 100644 index 0bd9162db7..0000000000 --- a/server/src/migrations/1700345818045-SystemMetadata.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SystemMetadata1700345818045 implements MigrationInterface { - name = 'SystemMetadata1700345818045' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "system_metadata" ("key" character varying NOT NULL, "value" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_fa94f6857470fb5b81ec6084465" PRIMARY KEY ("key"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_metadata"`); - } - -} diff --git a/server/src/migrations/1700362016675-Geodata.ts b/server/src/migrations/1700362016675-Geodata.ts deleted file mode 100644 index fa948e0896..0000000000 --- a/server/src/migrations/1700362016675-Geodata.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Geodata1700362016675 implements MigrationInterface { - name = 'Geodata1700362016675' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS cube`) - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS earthdistance`) - await queryRunner.query(`CREATE TABLE "geodata_admin2" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_admin1" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_places" ("id" integer NOT NULL, "name" character varying(200) NOT NULL, "longitude" double precision NOT NULL, "latitude" double precision NOT NULL, "countryCode" character(2) NOT NULL, "admin1Code" character varying(20), "admin2Code" character varying(80), "admin1Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, "admin2Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED, "modificationDate" date NOT NULL, CONSTRAINT "PK_c29918988912ef4036f3d7fbff4" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`) - await queryRunner.query(`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" USING gist ("earthCoord");`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord"`); - await queryRunner.query(`DROP TABLE "geodata_places"`); - await queryRunner.query(`DROP TABLE "geodata_admin1"`); - await queryRunner.query(`DROP TABLE "geodata_admin2"`); - await queryRunner.query(`DROP EXTENSION cube`); - await queryRunner.query(`DROP EXTENSION earthdistance`); - } - -} diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts deleted file mode 100644 index 4511e1001b..0000000000 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { getCLIPModelInfo } from 'src/utils/misc'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UsePgVectors1700713871511 implements MigrationInterface { - name = 'UsePgVectors1700713871511'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${await getVectorExtension(queryRunner)}`); - const faceDimQuery = await queryRunner.query(` - SELECT CARDINALITY(embedding::real[]) as dimsize - FROM asset_faces - LIMIT 1`); - const faceDimSize = faceDimQuery?.[0]?.['dimsize'] ?? 512; - - const clipModelNameQuery = await queryRunner.query( - `SELECT value FROM system_config WHERE key = 'machineLearning.clip.modelName'`, - ); - const clipModelName: string = clipModelNameQuery?.[0]?.['value'] ?? 'ViT-B-32__openai'; - const clipDimSize = getCLIPModelInfo(clipModelName.replaceAll('"', '')).dimSize; - - await queryRunner.query(` - ALTER TABLE asset_faces - ALTER COLUMN embedding SET NOT NULL, - ALTER COLUMN embedding TYPE vector(${faceDimSize})`); - - await queryRunner.query(` - CREATE TABLE smart_search ( - "assetId" uuid PRIMARY KEY NOT NULL REFERENCES assets(id) ON DELETE CASCADE, - embedding vector(${clipDimSize}) NOT NULL )`); - - await queryRunner.query(` - INSERT INTO smart_search("assetId", embedding) - SELECT si."assetId", si."clipEmbedding" - FROM smart_info si - WHERE "clipEmbedding" IS NOT NULL - AND CARDINALITY("clipEmbedding"::real[]) = ${clipDimSize} - AND array_position(si."clipEmbedding", NULL) IS NULL`); - - await queryRunner.query(`ALTER TABLE smart_info DROP COLUMN IF EXISTS "clipEmbedding"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE asset_faces ALTER COLUMN embedding TYPE real array`); - await queryRunner.query(`ALTER TABLE smart_info ADD COLUMN IF NOT EXISTS "clipEmbedding" TYPE real array`); - await queryRunner.query(` - INSERT INTO smart_info - ("assetId", "clipEmbedding") - SELECT s."assetId", s.embedding - FROM smart_search s - ON CONFLICT (s."assetId") DO UPDATE SET "clipEmbedding" = s.embedding`); - await queryRunner.query(`DROP TABLE IF EXISTS smart_search`); - } -} diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts deleted file mode 100644 index 43809d6364..0000000000 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { - name = 'AddCLIPEmbeddingIndex1700713994428'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS clip_index`); - } -} diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts deleted file mode 100644 index 5ee91afbcc..0000000000 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { - name = 'AddFaceEmbeddingIndex1700714033632'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS face_index`); - } -} diff --git a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts b/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts deleted file mode 100644 index b850d3da09..0000000000 --- a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSmartInfoTagsIndex1700714072055 implements MigrationInterface { - name = 'AddSmartInfoTagsIndex1700714072055'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS si_tags ON smart_info USING GIN (tags);`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS si_tags;`); - } -} diff --git a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts b/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts deleted file mode 100644 index b42291f6fd..0000000000 --- a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTextSearchIndex1700714140297 implements MigrationInterface { - name = 'CreateSmartInfoTextSearchIndex1700714140297'; - - public async up(queryRunner: QueryRunner): Promise { - // https://dba.stackexchange.com/a/164081 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_concat_ws(text, text[]) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE AS - 'SELECT array_to_string($2, $1)'`); - - await queryRunner.query(` - ALTER TABLE smart_info ADD "smartInfoTextSearchableColumn" tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR( - 'english', - f_concat_ws( - ' '::text, - COALESCE(tags, array[]::text[]) || COALESCE(objects, array[]::text[]) - ) - ) - ) - STORED NOT NULL`); - - await queryRunner.query(` - CREATE INDEX smart_info_text_searchable_idx - ON smart_info - USING GIN ("smartInfoTextSearchableColumn")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP FUNCTION IF EXISTS immutable_concat_ws`); - await queryRunner.query(`ALTER TABLE smart_info DROP IF EXISTS "smartInfoTextSearchableColumn"`); - } -} diff --git a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts b/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts deleted file mode 100644 index 38dd915139..0000000000 --- a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFaceIndicies1700752078178 implements MigrationInterface { - name = 'AddAssetFaceIndicies1700752078178' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_bf339a24070dac7e71304ec530" ON "asset_faces" ("personId", "assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`DROP INDEX "IDX_bf339a24070dac7e71304ec530"`); - } - -} diff --git a/server/src/migrations/1701665867595-AddExifCityIndex.ts b/server/src/migrations/1701665867595-AddExifCityIndex.ts deleted file mode 100644 index 0899ea1e6b..0000000000 --- a/server/src/migrations/1701665867595-AddExifCityIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifCityIndex1701665867595 implements MigrationInterface { - name = 'AddExifCityIndex1701665867595' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "exif_city" ON "exif" ("city") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "exif_city"`); - } - -} diff --git a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts b/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts deleted file mode 100644 index c2fc0b222f..0000000000 --- a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddWebSocketAttachmentTable1702084989965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'CREATE TABLE IF NOT EXISTS "socket_io_attachments" (id bigserial UNIQUE, created_at timestamptz DEFAULT NOW(), payload bytea);', - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "socket_io_attachments"`); - } -} diff --git a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts b/server/src/migrations/1702257380990-DropNullIslandLatLong.ts deleted file mode 100644 index 173b2c5950..0000000000 --- a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropNullIslandLatLong1702257380990 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;', - ); - } - - public async down(): Promise { - // There's no way to know which assets used to have 0/0 lat-long if we've - // already run this migration. - } -} diff --git a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts b/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts deleted file mode 100644 index c646287c81..0000000000 --- a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullifyFutureBirthDatesAndAddCheckConstraint1702938928766 implements MigrationInterface { - name = 'NullifyFutureBirthDatesAndAddCheckConstraint1702938928766' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "person" SET "birthDate" = NULL WHERE "birthDate" > CURRENT_DATE;`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45" CHECK ("birthDate" <= CURRENT_DATE)`); - } - - public async down(queryRunner: QueryRunner): Promise { - // The down method cannot revert the nullified dates - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45"`); - } - -} diff --git a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts b/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts deleted file mode 100644 index a55b12fa74..0000000000 --- a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixRemovedAssetsSharedLink1702942303661 implements MigrationInterface { - name = 'FixRemovedAssetsSharedLink1702942303661' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1703035138085-AddAutoStackId.ts b/server/src/migrations/1703035138085-AddAutoStackId.ts deleted file mode 100644 index d8c83ac565..0000000000 --- a/server/src/migrations/1703035138085-AddAutoStackId.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAutoStackId1703035138085 implements MigrationInterface { - name = 'AddAutoStackId1703035138085' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "autoStackId" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_auto_stack_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`); - } - -} diff --git a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts b/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts deleted file mode 100644 index 4ea88db8eb..0000000000 --- a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DefaultStorageTemplateOnForExistingInstallations1703288449127 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`) - if(adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_config (key, value) VALUES ('storageTemplate.enabled', 'true')`) - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_config WHERE key = 'storageTemplate.enabled'`) - } - -} diff --git a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts b/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts deleted file mode 100644 index 5d6477b8fb..0000000000 --- a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddQuotaColumnsToUser1704382918223 implements MigrationInterface { - name = 'AddQuotaColumnsToUser1704382918223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "quotaSizeInBytes" bigint`); - await queryRunner.query(`ALTER TABLE "users" ADD "quotaUsageInBytes" bigint NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaUsageInBytes"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaSizeInBytes"`); - } - -} diff --git a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts b/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts deleted file mode 100644 index cd737b2a62..0000000000 --- a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DefaultOnboardingForExistingInstallations1704571051932 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`); - if (adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_metadata (key, value) VALUES ($1, $2)`, [ - 'admin-onboarding', - String.raw`"{\"isOnboarded\":true}"`, - ]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_metadata WHERE key = 'admin-onboarding'`); - } -} diff --git a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts b/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts deleted file mode 100644 index 7b03ee9afb..0000000000 --- a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class SetAssetFaceNullOnPersonDelete1704943345360 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE CASCADE ON UPDATE CASCADE - `); - } - -} diff --git a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts b/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts deleted file mode 100644 index e76d095c10..0000000000 --- a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMetadataExtractedAt1705094221536 implements MigrationInterface { - name = 'AddMetadataExtractedAt1705094221536'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "metadataExtractedAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(` - UPDATE "asset_job_status" - SET "metadataExtractedAt" = NOW() - FROM "exif" - WHERE "exif"."assetId" = "asset_job_status"."assetId"; -`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "metadataExtractedAt"`); - } -} diff --git a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts b/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts deleted file mode 100644 index c62c01f50c..0000000000 --- a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameIndex1705306747072 implements MigrationInterface { - name = 'AddOriginalFileNameIndex1705306747072'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_4d66e76dada1ca180f67a205dc" ON "assets" ("originalFileName") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_4d66e76dada1ca180f67a205dc"`); - } -} diff --git a/server/src/migrations/1705363967169-CreateAssetStackTable.ts b/server/src/migrations/1705363967169-CreateAssetStackTable.ts deleted file mode 100644 index d1591797ff..0000000000 --- a/server/src/migrations/1705363967169-CreateAssetStackTable.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetStackTable1705197515600 implements MigrationInterface { - name = 'CreateAssetStackTable1705197515600'; - - public async up(queryRunner: QueryRunner): Promise { - // create table - await queryRunner.query( - `CREATE TABLE "asset_stack" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "primaryAssetId" uuid NOT NULL, - CONSTRAINT "REL_91704e101438fd0653f582426d" UNIQUE ("primaryAssetId"), - CONSTRAINT "PK_74a27e7fcbd5852463d0af3034b" PRIMARY KEY ("id"))`, - ); - - // create stacks - await queryRunner.query( - `INSERT INTO "asset_stack" ("primaryAssetId") - SELECT DISTINCT("stackParentId" ) - FROM "assets" - WHERE "stackParentId" IS NOT NULL;`, - ); - - // add "stackId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackId" uuid`); - - // set "stackId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."id" = "asset_stack"."primaryAssetId"`, - ); - - // set "stackId" for children - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."stackParentId" = "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_91704e101438fd0653f582426dc" FOREIGN KEY ("primaryAssetId") REFERENCES "assets"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - // drop "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // add "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackParentId" uuid`); - - // set "stackParentId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackParentId" = "asset_stack"."primaryAssetId" - FROM "asset_stack" - WHERE "assets"."stackId" = "asset_stack"."id" and "assets"."id" != "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_91704e101438fd0653f582426dc"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207"`); - - // drop table - await queryRunner.query(`DROP TABLE "asset_stack"`); - - // drop "stackId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackId"`); - } -} diff --git a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts b/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts deleted file mode 100644 index 11c84cf970..0000000000 --- a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddVectorsToSearchPath1707000751533 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const res = await queryRunner.query(`SELECT current_database() as db`); - const databaseName = res[0]['db']; - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`); - } - - public async down(queryRunner: QueryRunner): Promise { - const databaseName = await queryRunner.query(`SELECT current_database()`); - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public`); - } -} diff --git a/server/src/migrations/1708059341865-GeodataLocationSearch.ts b/server/src/migrations/1708059341865-GeodataLocationSearch.ts deleted file mode 100644 index af2d5dc5f3..0000000000 --- a/server/src/migrations/1708059341865-GeodataLocationSearch.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataLocationSearch1708059341865 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS unaccent`); - - // https://stackoverflow.com/a/11007216 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_unaccent(text) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT - RETURN unaccent('unaccent', $1)`); - - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin1Name" varchar`); - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin2Name" varchar`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key"`); - - await queryRunner.query(`DROP TABLE geodata_admin1 CASCADE`); - await queryRunner.query(`DROP TABLE geodata_admin2 CASCADE`); - - await queryRunner.query(` - ALTER TABLE geodata_places - DROP COLUMN "admin1Key", - DROP COLUMN "admin2Key"`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_name - ON geodata_places - USING gin (f_unaccent(name) gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin1_name - ON geodata_places - USING gin (f_unaccent("admin1Name") gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_name - ON geodata_places - USING gin (f_unaccent("admin2Name") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "geodata_admin1" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - CREATE TABLE "geodata_admin2" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - ALTER TABLE geodata_places - ADD COLUMN "admin1Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, - ADD COLUMN "admin2Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED`); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin1" - SELECT DISTINCT - "admin1Key" AS "key", - "admin1Name" AS "name" - FROM geodata_places - WHERE "admin1Name" IS NOT NULL`, - ); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin2" - SELECT DISTINCT - "admin2Key" AS "key", - "admin2Name" AS "name" - FROM geodata_places - WHERE "admin2Name" IS NOT NULL`, - ); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key";`); - } -} diff --git a/server/src/migrations/1708116312820-GeonamesEnhancement.ts b/server/src/migrations/1708116312820-GeonamesEnhancement.ts deleted file mode 100644 index 0cea9a0411..0000000000 --- a/server/src/migrations/1708116312820-GeonamesEnhancement.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class GeonamesEnhancement1708116312820 implements MigrationInterface { - name = 'GeonamesEnhancement1708116312820' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "alternateNames" varchar`); - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_alternate_names - ON geodata_places - USING gin (f_unaccent("alternateNames") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "alternateNames"`); - } - -} diff --git a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts b/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts deleted file mode 100644 index f7ca40cd46..0000000000 --- a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFileCreatedAtIndex1708227417898 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX idx_asset_file_created_at ON assets ("fileCreatedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_asset_file_created_at`); - } -} diff --git a/server/src/migrations/1708425975121-RemoveExternalPath.ts b/server/src/migrations/1708425975121-RemoveExternalPath.ts deleted file mode 100644 index 6c43e351f9..0000000000 --- a/server/src/migrations/1708425975121-RemoveExternalPath.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveExternalPath1708425975121 implements MigrationInterface { - name = 'RemoveExternalPath1708425975121'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } -} diff --git a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts b/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts deleted file mode 100644 index 8b7ff3a675..0000000000 --- a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts b/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts deleted file mode 100644 index 1d4f13410a..0000000000 --- a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetOriginalPathTrigramIndex1709608140355 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX idx_originalFileName_trigram - ON assets - USING gin (f_unaccent("originalFileName") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "idx_originalFileName_trigram"`); - } -} diff --git a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts b/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts deleted file mode 100644 index d1f73b4e3b..0000000000 --- a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExtensionToOriginalFileName1709763765506 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - WITH extension AS (WITH cte AS (SELECT a.id, STRING_TO_ARRAY(a."originalPath", '.')::TEXT[] AS arr - FROM assets a) - SELECT cte.id, cte.arr[ARRAY_UPPER(cte.arr, 1)] AS "ext" - FROM cte) - UPDATE assets - SET "originalFileName" = assets."originalFileName" || '.' || extension."ext" - FROM extension - WHERE assets.id = extension.id; - `); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts b/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts deleted file mode 100644 index 9689741929..0000000000 --- a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CascadeSharedLinksDelete1709825430031 implements MigrationInterface { - name = 'CascadeSharedLinksDelete1709825430031' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1709870213078-AddUserStatus.ts b/server/src/migrations/1709870213078-AddUserStatus.ts deleted file mode 100644 index 858f51258f..0000000000 --- a/server/src/migrations/1709870213078-AddUserStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserStatus1709870213078 implements MigrationInterface { - name = 'AddUserStatus1709870213078' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "status" character varying NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "status"`); - } - -} diff --git a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts b/server/src/migrations/1710182081326-AscendingOrderAlbum.ts deleted file mode 100644 index b672ff2b20..0000000000 --- a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AscendingOrderAlbum1710182081326 implements MigrationInterface { - name = 'AscendingOrderAlbum1710182081326' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "order" character varying NOT NULL DEFAULT 'desc'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "order"`); - } - -} diff --git a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts b/server/src/migrations/1710293990203-AddAssetRelationIndices.ts deleted file mode 100644 index dd0abf7fd5..0000000000 --- a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetRelationIndices1710293990203 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`DROP INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`DROP INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts b/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts deleted file mode 100644 index ab6f2a4e9f..0000000000 --- a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameWebpJpegPaths1711257900274 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath'); - await queryRunner.renameColumn('assets', 'resizePath', 'previewPath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'image.previewSize' - WHERE key = 'thumbnail.jpegSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.thumbnailSize' - WHERE key = 'thumbnail.webpSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.quality' - WHERE key = 'thumbnail.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.colorspace' - WHERE key = 'thumbnail.colorspace'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath'); - await queryRunner.renameColumn('assets', 'previewPath', 'resizePath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'thumbnail.jpegSize' - WHERE key = 'image.previewSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.webpSize' - WHERE key = 'image.thumbnailSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.quality' - WHERE key = 'image.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.colorspace' - WHERE key = 'image.colorspace'`, - ); - } -} diff --git a/server/src/migrations/1711637874206-AddMemoryTable.ts b/server/src/migrations/1711637874206-AddMemoryTable.ts deleted file mode 100644 index b1c5b437d7..0000000000 --- a/server/src/migrations/1711637874206-AddMemoryTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryTable1711637874206 implements MigrationInterface { - name = 'AddMemoryTable1711637874206' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "memories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "data" jsonb NOT NULL, "isSaved" boolean NOT NULL DEFAULT false, "memoryAt" TIMESTAMP WITH TIME ZONE NOT NULL, "seenAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_aaa0692d9496fe827b0568612f8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "memories_assets_assets" ("memoriesId" uuid NOT NULL, "assetsId" uuid NOT NULL, CONSTRAINT "PK_fcaf7112a013d1703c011c6793d" PRIMARY KEY ("memoriesId", "assetsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_984e5c9ab1f04d34538cd32334" ON "memories_assets_assets" ("memoriesId") `); - await queryRunner.query(`CREATE INDEX "IDX_6942ecf52d75d4273de19d2c16" ON "memories_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "memories" ADD CONSTRAINT "FK_575842846f0c28fa5da46c99b19" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e" FOREIGN KEY ("memoriesId") REFERENCES "memories"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`); - await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`); - await queryRunner.query(`DROP INDEX "IDX_6942ecf52d75d4273de19d2c16"`); - await queryRunner.query(`DROP INDEX "IDX_984e5c9ab1f04d34538cd32334"`); - await queryRunner.query(`DROP TABLE "memories_assets_assets"`); - await queryRunner.query(`DROP TABLE "memories"`); - } - -} diff --git a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts b/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts deleted file mode 100644 index d295ec2d7c..0000000000 --- a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetDuplicateColumns1711989989911 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets ADD COLUMN "duplicateId" uuid`); - await queryRunner.query(`ALTER TABLE asset_job_status ADD COLUMN "duplicatesDetectedAt" timestamptz`); - await queryRunner.query(`CREATE INDEX "IDX_assets_duplicateId" ON assets ("duplicateId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets DROP COLUMN "duplicateId"`); - await queryRunner.query(`ALTER TABLE asset_job_status DROP COLUMN "duplicatesDetectedAt"`); - } -} diff --git a/server/src/migrations/1713337511945-AddAlbumUserRole.ts b/server/src/migrations/1713337511945-AddAlbumUserRole.ts deleted file mode 100644 index a8d0d3d685..0000000000 --- a/server/src/migrations/1713337511945-AddAlbumUserRole.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAlbumUserRole1713337511945 implements MigrationInterface { - name = 'AddAlbumUserRole1713337511945' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "role" character varying NOT NULL DEFAULT 'editor'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "role"`); - } - -} diff --git a/server/src/migrations/1713490844785-RenameSessionsTable.ts b/server/src/migrations/1713490844785-RenameSessionsTable.ts deleted file mode 100644 index b1b35e8ae6..0000000000 --- a/server/src/migrations/1713490844785-RenameSessionsTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSessionsTable1713490844785 implements MigrationInterface { - name = 'RenameSessionsTable1713490844785'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" RENAME TO "sessions"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" to "FK_57de40bc620f456c7311aa3a1e6"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_57de40bc620f456c7311aa3a1e6" to "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME TO "user_token"`); - } -} diff --git a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts b/server/src/migrations/1714698592332-RemoveIsReadOnly.ts deleted file mode 100644 index bc56f8dac7..0000000000 --- a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveIsReadOnly1714698592332 implements MigrationInterface { - name = 'RemoveIsReadOnly1714698592332' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - } - -} diff --git a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts b/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts deleted file mode 100644 index f037ba1fb0..0000000000 --- a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MotionAssetExtensionMP41715435221124 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "assets" SET "originalFileName" = regexp_replace("originalFileName", '\\.[a-zA-Z0-9]+$', '.mp4') WHERE "originalPath" LIKE '%.mp4' AND "isVisible" = false`, - ); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts b/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts deleted file mode 100644 index 50e99f0b2a..0000000000 --- a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveTextSearchColumn1715623169039 implements MigrationInterface { - name = 'RemoveTextSearchColumn1715623169039' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - } - -} diff --git a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts b/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts deleted file mode 100644 index c16eec7160..0000000000 --- a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'lodash'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveSystemConfigTable1715787369686 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const overrides = await queryRunner.query('SELECT "key", "value" FROM "system_config"'); - if (overrides.length === 0) { - return; - } - - const config = {}; - for (const { key, value } of overrides) { - _.set(config, key, JSON.parse(value)); - } - - await queryRunner.query(`INSERT INTO "system_metadata" ("key", "value") VALUES ($1, $2)`, [ - 'system-config', - // yup, we're double-stringifying it - JSON.stringify(JSON.stringify(config)), - ]); - - await queryRunner.query(`DROP TABLE "system_config"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // no data restore, you just get the table back - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } -} diff --git a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts b/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts deleted file mode 100644 index 45f5248c1a..0000000000 --- a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveLibraryIsVisible1715798702876 implements MigrationInterface { - name = 'RemoveLibraryIsVisible1715798702876' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "isVisible"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" ADD "isVisible" boolean NOT NULL DEFAULT true`); - } - -} diff --git a/server/src/migrations/1715804005643-RemoveLibraryType.ts b/server/src/migrations/1715804005643-RemoveLibraryType.ts deleted file mode 100644 index cd4dc574f2..0000000000 --- a/server/src/migrations/1715804005643-RemoveLibraryType.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryType1715804005643 implements MigrationInterface { - name = 'RemoveLibraryType1715804005643'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(` - UPDATE "assets" - SET "libraryId" = NULL - FROM "libraries" - WHERE "assets"."libraryId" = "libraries"."id" - AND "libraries"."type" = 'UPLOAD' -`); - await queryRunner.query(`DELETE FROM "libraries" WHERE "type" = 'UPLOAD'`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "type"`); - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_checksum" ON "assets" ("ownerId", "checksum") WHERE "libraryId" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" ON "assets" ("ownerId", "libraryId", "checksum") WHERE "libraryId" IS NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1715890481637-FixJsonB.ts b/server/src/migrations/1715890481637-FixJsonB.ts deleted file mode 100644 index afb39565a3..0000000000 --- a/server/src/migrations/1715890481637-FixJsonB.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixJsonB1715890481637 implements MigrationInterface { - name = 'FixJsonB1715890481637'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" DROP DEFAULT`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [value, key]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" SET DEFAULT '{}'`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [ - JSON.stringify(JSON.stringify(value)), - key, - ]); - } - } -} diff --git a/server/src/migrations/1716312279245-UserMetadata.ts b/server/src/migrations/1716312279245-UserMetadata.ts deleted file mode 100644 index b118a8d42a..0000000000 --- a/server/src/migrations/1716312279245-UserMetadata.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMetadata1716312279245 implements MigrationInterface { - name = 'UserMetadata1716312279245'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "user_metadata" ("userId" uuid NOT NULL, "key" character varying NOT NULL, "value" jsonb NOT NULL, CONSTRAINT "PK_5931462150b3438cbc83277fe5a" PRIMARY KEY ("userId", "key"))`, - ); - const users = await queryRunner.query('SELECT "id", "memoriesEnabled", "avatarColor" FROM "users"'); - for (const { id, memoriesEnabled, avatarColor } of users) { - const preferences: any = {}; - if (!memoriesEnabled) { - preferences.memories = { enabled: false }; - } - - if (avatarColor) { - preferences.avatar = { color: avatarColor }; - } - - if (Object.keys(preferences).length === 0) { - continue; - } - - await queryRunner.query('INSERT INTO "user_metadata" ("userId", "key", "value") VALUES ($1, $2, $3)', [ - id, - 'preferences', - preferences, - ]); - } - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - await queryRunner.query( - `ALTER TABLE "user_metadata" ADD CONSTRAINT "FK_6afb43681a21cf7815932bc38ac" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_metadata" DROP CONSTRAINT "FK_6afb43681a21cf7815932bc38ac"`); - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - const items = await queryRunner.query( - `SELECT "userId" as "id", "value" FROM "user_metadata" WHERE "key"='preferences'`, - ); - for (const { id, value } of items) { - if (!value) { - continue; - } - - if (value.avatar?.color) { - await queryRunner.query(`UPDATE "users" SET "avatarColor" = $1 WHERE "id" = $2`, [value.avatar.color, id]); - } - - if (value.memories?.enabled === false) { - await queryRunner.query(`UPDATE "users" SET "memoriesEnabled" = false WHERE "id" = $1`, [id]); - } - } - await queryRunner.query(`DROP TABLE "user_metadata"`); - } -} diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts deleted file mode 100644 index 2bd1acad34..0000000000 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { DatabaseExtension } from 'src/enum'; -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceSearchRelation1718486162779 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - const hasEmbeddings = async (tableName: string): Promise => { - const columns = await queryRunner.query( - `SELECT column_name as name - FROM information_schema.columns - WHERE table_name = '${tableName}'`, - ); - return columns.some((column: { name: string }) => column.name === 'embedding'); - }; - - const hasAssetEmbeddings = await hasEmbeddings('smart_search'); - if (!hasAssetEmbeddings) { - await queryRunner.query(`TRUNCATE smart_search`); - await queryRunner.query(`ALTER TABLE smart_search ADD COLUMN IF NOT EXISTS embedding vector(512) NOT NULL`); - } - - await queryRunner.query(` - CREATE TABLE face_search ( - "faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE, - embedding vector(512) NOT NULL )`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - - const hasFaceEmbeddings = await hasEmbeddings('asset_faces'); - if (hasFaceEmbeddings) { - await queryRunner.query(` - INSERT INTO face_search("faceId", embedding) - SELECT id, embedding - FROM asset_faces faces`); - } - - await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN IF EXISTS embedding`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(` - UPDATE asset_faces - SET embedding = fs.embedding - FROM face_search fs - WHERE id = fs."faceId"`); - await queryRunner.query(`DROP TABLE face_search`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } -} diff --git a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts b/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts deleted file mode 100644 index 9bf2a2b8d7..0000000000 --- a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixLivePhotoVideoRelation1719359859887 implements MigrationInterface { - name = 'FixLivePhotoVideoRelation1719359859887' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - -} diff --git a/server/src/migrations/1720207981949-AddStackOwner.ts b/server/src/migrations/1720207981949-AddStackOwner.ts deleted file mode 100644 index 61394cc985..0000000000 --- a/server/src/migrations/1720207981949-AddStackOwner.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackOwner1720207981949 implements MigrationInterface { - name = 'AddStackOwner1720207981949' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" ADD "ownerId" uuid`); - await queryRunner.query(` - UPDATE "asset_stack" stack - SET "ownerId" = asset."ownerId" - FROM "assets" asset - WHERE stack."primaryAssetId" = asset."id" - `) - await queryRunner.query('ALTER TABLE "asset_stack" ALTER COLUMN "ownerId" SET NOT NULL') - await queryRunner.query(`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8"`); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP COLUMN "ownerId"`); - } -} diff --git a/server/src/migrations/1720375641148-natural-earth-countries.ts b/server/src/migrations/1720375641148-natural-earth-countries.ts deleted file mode 100644 index 8c58321dca..0000000000 --- a/server/src/migrations/1720375641148-natural-earth-countries.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NaturalEarthCountries1720375641148 implements MigrationInterface { - name = 'NaturalEarthCountries1720375641148' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "naturalearth_countries" ("id" SERIAL NOT NULL, "admin" character varying(50) NOT NULL, "admin_a3" character varying(3) NOT NULL, "type" character varying(50) NOT NULL, "coordinates" polygon NOT NULL, CONSTRAINT "PK_21a6d86d1ab5d841648212e5353" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "naturalearth_countries"`); - } - -} diff --git a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts b/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts deleted file mode 100644 index 7f185077ff..0000000000 --- a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSourceColumnToAssetFace1721249222549 implements MigrationInterface { - name = 'AddSourceColumnToAssetFace1721249222549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "sourceType" sourceType NOT NULL DEFAULT 'machine-learning'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "sourceType"`); - await queryRunner.query(`DROP TYPE sourceType`); - } - -} diff --git a/server/src/migrations/1722753178937-AddExifRating.ts b/server/src/migrations/1722753178937-AddExifRating.ts deleted file mode 100644 index 52e8fb71e8..0000000000 --- a/server/src/migrations/1722753178937-AddExifRating.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddRating1722753178937 implements MigrationInterface { - name = 'AddRating1722753178937' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "rating" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "rating"`); - } - -} diff --git a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts b/server/src/migrations/1723719333525-AddApiKeyPermissions.ts deleted file mode 100644 index d585d98bcb..0000000000 --- a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddApiKeyPermissions1723719333525 implements MigrationInterface { - name = 'AddApiKeyPermissions1723719333525'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" ADD "permissions" character varying array NOT NULL DEFAULT '{all}'`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "permissions" DROP DEFAULT`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "permissions"`); - } -} diff --git a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts deleted file mode 100644 index a71ddfbcf3..0000000000 --- a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbnailJobStatus1724080823160 implements MigrationInterface { - name = 'AddThumbnailJobStatus1724080823160'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "previewAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "thumbnailAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."previewPath" IS NOT NULL`); - await queryRunner.query(`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."thumbnailPath" IS NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "thumbnailAt"`); - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "previewAt"`); - } -} diff --git a/server/src/migrations/1724101822106-AddAssetFilesTable.ts b/server/src/migrations/1724101822106-AddAssetFilesTable.ts deleted file mode 100644 index bb086b084e..0000000000 --- a/server/src/migrations/1724101822106-AddAssetFilesTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFilesTable1724101822106 implements MigrationInterface { - name = 'AddAssetFilesTable1724101822106' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_files" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "assetId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "type" character varying NOT NULL, "path" character varying NOT NULL, CONSTRAINT "UQ_assetId_type" UNIQUE ("assetId", "type"), CONSTRAINT "PK_c41dc3e9ef5e1c57ca5a08a0004" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_assetId" ON "asset_files" ("assetId") `); - await queryRunner.query(`ALTER TABLE "asset_files" ADD CONSTRAINT "FK_e3e103a5f1d8bc8402999286040" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - // preview path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'preview', "previewPath" FROM "assets" WHERE "previewPath" IS NOT NULL AND "previewPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "previewPath"`); - - // thumbnail path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'thumbnail', "thumbnailPath" FROM "assets" WHERE "thumbnailPath" IS NOT NULL AND "thumbnailPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbnailPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // undo preview path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "previewPath" character varying`); - await queryRunner.query(`UPDATE "assets" SET "previewPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'preview'`); - - // undo thumbnail path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbnailPath" character varying DEFAULT ''`); - await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`); - - await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`); - await queryRunner.query(`DROP INDEX "IDX_asset_files_assetId"`); - await queryRunner.query(`DROP TABLE "asset_files"`); - } - -} diff --git a/server/src/migrations/1724790460210-NestedTagTable.ts b/server/src/migrations/1724790460210-NestedTagTable.ts deleted file mode 100644 index d468ff6ba4..0000000000 --- a/server/src/migrations/1724790460210-NestedTagTable.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NestedTagTable1724790460210 implements MigrationInterface { - name = 'NestedTagTable1724790460210' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query('TRUNCATE TABLE "tags" CASCADE'); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_tag_name_userId"`); - await queryRunner.query(`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant"))`); - await queryRunner.query(`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor") `); - await queryRunner.query(`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant") `); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "renameTagId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "type"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "value" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - await queryRunner.query(`ALTER TABLE "tags" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "color" character varying`); - await queryRunner.query(`ALTER TABLE "tags" ADD "parentId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1"`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "parentId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "color"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "createdAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`); - await queryRunner.query(`DROP INDEX "IDX_b1a2a7ed45c29179b5ad51548a"`); - await queryRunner.query(`DROP INDEX "IDX_15fbcbc67663c6bfc07b354c22"`); - await queryRunner.query(`DROP TABLE "tags_closure"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1725023079109-FixTagUniqueness.ts b/server/src/migrations/1725023079109-FixTagUniqueness.ts deleted file mode 100644 index 859712621c..0000000000 --- a/server/src/migrations/1725023079109-FixTagUniqueness.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixTagUniqueness1725023079109 implements MigrationInterface { - name = 'FixTagUniqueness1725023079109' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_79d6f16e52bb2c7130375246793"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - } - -} diff --git a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts deleted file mode 100644 index 8eb47db438..0000000000 --- a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts b/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts deleted file mode 100644 index 98a3fe403a..0000000000 --- a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveThumbailAtForMissingThumbnails1725327902980 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "asset_job_status" j SET "thumbnailAt" = NULL WHERE j."thumbnailAt" IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM asset_files f WHERE j."assetId" = f."assetId" AND f."type" = 'thumbnail' AND f."path" IS NOT NULL )`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts b/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts deleted file mode 100644 index 2dfb5b7978..0000000000 --- a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveHiddenAssetsFromAlbums1725730782681 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "albums_assets_assets" WHERE "assetsId" IN (SELECT "id" FROM "assets" WHERE "isVisible" = false)`, - ); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1726491047923-AddprofileChangedAt.ts b/server/src/migrations/1726491047923-AddprofileChangedAt.ts deleted file mode 100644 index bcf568426a..0000000000 --- a/server/src/migrations/1726491047923-AddprofileChangedAt.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddprofileChangedAt1726491047923 implements MigrationInterface { - name = 'AddprofileChangedAt1726491047923' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "profileChangedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "profileChangedAt"`); - } - -} diff --git a/server/src/migrations/1726593009549-AddAssetStatus.ts b/server/src/migrations/1726593009549-AddAssetStatus.ts deleted file mode 100644 index 5b243b05b5..0000000000 --- a/server/src/migrations/1726593009549-AddAssetStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetStatus1726593009549 implements MigrationInterface { - name = 'AddAssetStatus1726593009549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "assets_status_enum" AS ENUM('active', 'trashed', 'deleted')`); - await queryRunner.query(`ALTER TABLE "assets" ADD "status" "assets_status_enum" NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "status"`); - await queryRunner.query(`DROP TYPE "assets_status_enum"`); - } - -} diff --git a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts b/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts deleted file mode 100644 index e02203997f..0000000000 --- a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class SeparateQualityForThumbnailAndPreview1727471863507 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls( - jsonb_build_object( - 'preview', jsonb_build_object( - 'format', value->'image'->'previewFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'previewSize'), - 'thumbnail', jsonb_build_object( - 'format', value->'image'->'thumbnailFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'thumbnailSize'), - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace' - ))) - where key = 'system-config'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls(jsonb_build_object( - 'previewFormat', value->'image'->'preview'->'format', - 'previewSize', value->'image'->'preview'->'size', - 'thumbnailFormat', value->'image'->'thumbnail'->'format', - 'thumbnailSize', value->'image'->'thumbnail'->'size', - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace', - 'quality', value->'image'->'preview'->'quality' - ))) - where key = 'system-config'`); - } -} diff --git a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts b/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts deleted file mode 100644 index 050e9a93cf..0000000000 --- a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class IsOfflineSetDeletedAt1727781844613 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = now() WHERE "isOffline" = true AND "deletedAt" IS NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = null WHERE "isOffline" = true`, - ); - } -} diff --git a/server/src/migrations/1727797340951-AddVersionHistory.ts b/server/src/migrations/1727797340951-AddVersionHistory.ts deleted file mode 100644 index 7eb731d1a3..0000000000 --- a/server/src/migrations/1727797340951-AddVersionHistory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddVersionHistory1727797340951 implements MigrationInterface { - name = 'AddVersionHistory1727797340951' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "version_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "version" character varying NOT NULL, CONSTRAINT "PK_5db259cbb09ce82c0d13cfd1b23" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "version_history"`); - } - -} diff --git a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts b/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts deleted file mode 100644 index 280b34890d..0000000000 --- a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumAssetCreatedAt1729793521993 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "albums_assets_assets" ADD COLUMN "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "createdAt"`); - } -} diff --git a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts b/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts deleted file mode 100644 index 2c929191dd..0000000000 --- a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveNplFromSystemConfig1730227312171 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = value #- '{ffmpeg,npl}' - where key = 'system-config' and value->'ffmpeg'->'npl' is not null`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1730989238718-DropSmartInfoTable.ts b/server/src/migrations/1730989238718-DropSmartInfoTable.ts deleted file mode 100644 index a4de2652d6..0000000000 --- a/server/src/migrations/1730989238718-DropSmartInfoTable.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropSmartInfoTable1730989238718 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE smart_info`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts b/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts deleted file mode 100644 index 3ebe8108cb..0000000000 --- a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class NaturalEarthCountriesIdentityColumn1732072134943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP DEFAULT`); - await queryRunner.query(`DROP SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id ADD GENERATED ALWAYS AS IDENTITY`); - - // same as ll_to_earth, but with explicit schema to avoid weirdness and allow it to work in expression indices - await queryRunner.query(` - CREATE FUNCTION ll_to_earth_public(latitude double precision, longitude double precision) RETURNS public.earth PARALLEL SAFE IMMUTABLE STRICT LANGUAGE SQL AS $$ - SELECT public.cube(public.cube(public.cube(public.earth()*cos(radians(latitude))*cos(radians(longitude))),public.earth()*cos(radians(latitude))*sin(radians(longitude))),public.earth()*sin(radians(latitude)))::public.earth - $$`); - - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "earthCoord"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP GENERATED`); - await queryRunner.query(`CREATE SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query( - `ALTER TABLE naturalearth_countries ALTER id SET DEFAULT nextval('naturalearth_countries_id_seq'::regclass)`, - ); - await queryRunner.query(`DROP FUNCTION ll_to_earth_public`); - await queryRunner.query( - `ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`, - ); - } -} diff --git a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts b/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts deleted file mode 100644 index 65bb02c8e2..0000000000 --- a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameMachineLearningUrlToUrls1733339482860 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,url}', '{machineLearning,urls}'::text[], jsonb_build_array(value->'machineLearning'->'url')) - WHERE key = 'system-config' AND value->'machineLearning'->'url' IS NOT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,urls}', '{machineLearning,url}'::text[], to_jsonb(value->'machineLearning'->'urls'->>0)) - WHERE key = 'system-config' AND value->'machineLearning'->'urls' IS NOT NULL AND jsonb_array_length(value->'machineLearning'->'urls') >= 1 - `); - } -} diff --git a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts b/server/src/migrations/1734574016301-AddTimeBucketIndices.ts deleted file mode 100644 index 2162a713fc..0000000000 --- a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTimeBucketIndices1734574016301 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX idx_local_date_time_month ON assets ((date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX idx_local_date_time ON assets ((("localDateTime" at time zone 'UTC')::date))`, - ); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_local_date_time_month`); - await queryRunner.query(`DROP INDEX idx_local_date_time`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } -} diff --git a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts b/server/src/migrations/1734879118272-AddIsFavoritePerson.ts deleted file mode 100644 index 6f7640f96f..0000000000 --- a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsFavoritePerson1734879118272 implements MigrationInterface { - name = 'AddIsFavoritePerson1734879118272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isFavorite" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isFavorite"`); - } - -} diff --git a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts b/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts deleted file mode 100644 index 74dde826fb..0000000000 --- a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtTriggers1737672307560 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create function updated_at() - returns trigger as $$ - begin - new."updatedAt" = now(); - return new; - end; - $$ language 'plpgsql'`); - - await queryRunner.query(` - create trigger activity_updated_at - before update on activity - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger albums_updated_at - before update on albums - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger api_keys_updated_at - before update on api_keys - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger asset_files_updated_at - before update on asset_files - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger assets_updated_at - before update on assets - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger libraries_updated_at - before update on libraries - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger memories_updated_at - before update on memories - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger partners_updated_at - before update on partners - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger person_updated_at - before update on person - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger sessions_updated_at - before update on sessions - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger tags_updated_at - before update on tags - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger users_updated_at - before update on users - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger activity_updated_at on activity`); - await queryRunner.query(`drop trigger albums_updated_at on albums`); - await queryRunner.query(`drop trigger api_keys_updated_at on api_keys`); - await queryRunner.query(`drop trigger asset_files_updated_at on asset_files`); - await queryRunner.query(`drop trigger assets_updated_at on assets`); - await queryRunner.query(`drop trigger libraries_updated_at on libraries`); - await queryRunner.query(`drop trigger memories_updated_at on memories`); - await queryRunner.query(`drop trigger partners_updated_at on partners`); - await queryRunner.query(`drop trigger person_updated_at on person`); - await queryRunner.query(`drop trigger sessions_updated_at on sessions`); - await queryRunner.query(`drop trigger tags_updated_at on tags`); - await queryRunner.query(`drop trigger users_updated_at on users`); - await queryRunner.query(`drop function updated_at_trigger`); - } -} diff --git a/server/src/migrations/1737845696644-NullableDates.ts b/server/src/migrations/1737845696644-NullableDates.ts deleted file mode 100644 index 8a08b985c5..0000000000 --- a/server/src/migrations/1737845696644-NullableDates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullableDates1737845696644 implements MigrationInterface { - name = 'NullableDates1737845696644' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" DROP NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" SET NOT NULL`); - } - -} diff --git a/server/src/migrations/1738889177573-AddPersonColor.ts b/server/src/migrations/1738889177573-AddPersonColor.ts deleted file mode 100644 index ebdc86f52d..0000000000 --- a/server/src/migrations/1738889177573-AddPersonColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPersonColor1738889177573 implements MigrationInterface { - name = 'AddPersonColor1738889177573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "color" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "color"`); - } - -} diff --git a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts b/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts deleted file mode 100644 index e6f18e2618..0000000000 --- a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtColumnToAssetFacesTable1739466714036 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - ADD COLUMN "deletedAt" TIMESTAMP WITH TIME ZONE DEFAULT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - DROP COLUMN "deletedAt" - `); - } -} diff --git a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts b/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts deleted file mode 100644 index d53c7c17f6..0000000000 --- a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryShowHideDates1739824470990 implements MigrationInterface { - name = 'AddMemoryShowHideDates1739824470990' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" ADD "showAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "memories" ADD "hideAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "hideAt"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "showAt"`); - } - -} diff --git a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts b/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts deleted file mode 100644 index ef75dd7c0d..0000000000 --- a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSessionSyncCheckpointTable1740001232576 implements MigrationInterface { - name = 'AddSessionSyncCheckpointTable1740001232576' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "session_sync_checkpoints" ("sessionId" uuid NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ack" character varying NOT NULL, CONSTRAINT "PK_b846ab547a702863ef7cd9412fb" PRIMARY KEY ("sessionId", "type"))`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc" FOREIGN KEY ("sessionId") REFERENCES "sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(` - create trigger session_sync_checkpoints_updated_at - before update on session_sync_checkpoints - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger session_sync_checkpoints_updated_at on session_sync_checkpoints`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc"`); - await queryRunner.query(`DROP TABLE "session_sync_checkpoints"`); - } - -} diff --git a/server/src/migrations/1740064899123-AddUsersAuditTable.ts b/server/src/migrations/1740064899123-AddUsersAuditTable.ts deleted file mode 100644 index b8f2ce5e3a..0000000000 --- a/server/src/migrations/1740064899123-AddUsersAuditTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsersAuditTable1740064899123 implements MigrationInterface { - name = 'AddUsersAuditTable1740064899123' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_updated_at_asc_id_asc" ON "users" ("updatedAt" ASC, "id" ASC);`) - await queryRunner.query(`CREATE TABLE "users_audit" ("id" SERIAL NOT NULL, "userId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("deletedAt" ASC, "userId" ASC);`) - await queryRunner.query(`CREATE OR REPLACE FUNCTION users_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO users_audit ("userId") - SELECT "id" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER users_delete_audit`); - await queryRunner.query(`DROP FUNCTION users_delete_audit`); - await queryRunner.query(`DROP TABLE "users_audit"`); - } - -} diff --git a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts b/server/src/migrations/1740586617223-AddUpdateIdColumns.ts deleted file mode 100644 index 02d680ddf6..0000000000 --- a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUpdateIdColumns1740586617223 implements MigrationInterface { - name = 'AddUpdateIdColumns1740586617223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create or replace function immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) - returns uuid - as $$ - select encode( - set_bit( - set_bit( - overlay(uuid_send(gen_random_uuid()) - placing substring(int8send(floor(extract(epoch from p_timestamp) * 1000)::bigint) from 3) - from 1 for 6 - ), - 52, 1 - ), - 53, 1 - ), - 'hex')::uuid; - $$ - language SQL - volatile; - `) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - return new; - END; - $$; - `) - await queryRunner.query(`ALTER TABLE "person" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "asset_files" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "libraries" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "users" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "albums" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "sessions" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "partners" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "memories" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "activity" ADD "updateId" uuid`); - - await queryRunner.query(`UPDATE "person" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "asset_files" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "libraries" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "tags" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "assets" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "users" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "albums" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "sessions" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "session_sync_checkpoints" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "partners" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "memories" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "api_keys" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "activity" SET "updateId" = immich_uuid_v7("updatedAt")`); - - await queryRunner.query(`ALTER TABLE "person" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "asset_files" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "libraries" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "tags" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "partners" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "memories" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "activity" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - - await queryRunner.query(`CREATE INDEX "IDX_person_update_id" ON "person" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_update_id" ON "asset_files" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_libraries_update_id" ON "libraries" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_tags_update_id" ON "tags" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_assets_update_id" ON "assets" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_users_update_id" ON "users" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_albums_update_id" ON "albums" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_sessions_update_id" ON "sessions" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_partners_update_id" ON "partners" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_memories_update_id" ON "memories" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_api_keys_update_id" ON "api_keys" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_activity_update_id" ON "activity" ("updateId")`); - - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - DECLARE - clock_timestamp TIMESTAMP := clock_timestamp(); - BEGIN - new."updatedAt" = clock_timestamp; - new."updateId" = immich_uuid_v7(clock_timestamp); - return new; - END; - $$; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "asset_files" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "updateId"`); - await queryRunner.query(`DROP FUNCTION immich_uuid_v7`); - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - new."updatedAt" = now(); - return new; - END; - $$; - `) - } - -} diff --git a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts b/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts deleted file mode 100644 index 59fc4dbd5b..0000000000 --- a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UsersAuditUuidv7PrimaryKey1740595460866 implements MigrationInterface { - name = 'UsersAuditUuidv7PrimaryKey1740595460866' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at_asc_user_id_asc"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT clock_timestamp()`) - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at" ON "users_audit" ("deletedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT now()`); - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("userId", "deletedAt") `); - } - -} diff --git a/server/src/migrations/1740619600996-AddManualSourceType.ts b/server/src/migrations/1740619600996-AddManualSourceType.ts deleted file mode 100644 index dd53312ad7..0000000000 --- a/server/src/migrations/1740619600996-AddManualSourceType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddManualSourceType1740619600996 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TYPE sourceType ADD VALUE 'manual'`); - } - - public async down(queryRunner: QueryRunner): Promise { - // Prior to this migration, manually tagged pictures had the 'machine-learning' type - await queryRunner.query( - `UPDATE "asset_faces" SET "sourceType" = 'machine-learning' WHERE "sourceType" = 'manual';`, - ); - - // Postgres doesn't allow removing values from enums, we have to recreate the type - await queryRunner.query(`ALTER TYPE sourceType RENAME TO oldSourceType`); - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" DROP DEFAULT;`); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" TYPE sourceType USING "sourceType"::text::sourceType;`, - ); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" SET DEFAULT 'machine-learning'::sourceType;`, - ); - await queryRunner.query(`DROP TYPE oldSourceType;`); - } -} diff --git a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts b/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts deleted file mode 100644 index 5c735a60bb..0000000000 --- a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnsetStackedAssetsFromDuplicateStatus1740654480319 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update assets - set "duplicateId" = null - where "stackId" is not null`); - } - - public async down(): Promise { - // No need to revert this migration - } -} diff --git a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts b/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts deleted file mode 100644 index d9c9dc1949..0000000000 --- a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreatePartnersAuditTable1740739778549 implements MigrationInterface { - name = 'CreatePartnersAuditTable1740739778549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_952b50217ff78198a7e380f0359" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_by_id" ON "partners_audit" ("sharedById") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_with_id" ON "partners_audit" ("sharedWithId") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_deleted_at" ON "partners_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION partners_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO partners_audit ("sharedById", "sharedWithId") - SELECT "sharedById", "sharedWithId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_with_id"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_by_id"`); - await queryRunner.query(`DROP TRIGGER partners_delete_audit`); - await queryRunner.query(`DROP FUNCTION partners_delete_audit`); - await queryRunner.query(`DROP TABLE "partners_audit"`); - } - -} diff --git a/server/src/migrations/1741027685381-ResetMemories.ts b/server/src/migrations/1741027685381-ResetMemories.ts deleted file mode 100644 index 6a80372219..0000000000 --- a/server/src/migrations/1741027685381-ResetMemories.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ResetMemories1741027685381 implements MigrationInterface { - name = 'ResetMemories1741027685381'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "memories"`); - await queryRunner.query(`DELETE FROM "system_metadata" WHERE "key" = 'memories-state'`); - } - - public async down(): Promise { - // nothing to do - } -} diff --git a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts b/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts deleted file mode 100644 index 449272341c..0000000000 --- a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MoveHistoryUuidEntityId1741179334403 implements MigrationInterface { - name = 'MoveHistoryUuidEntityId1741179334403'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE uuid USING "entityId"::uuid;`); - await queryRunner.query(`delete from "move_history" - where - "move_history"."entityId" not in ( - select - "id" - from - "assets" - where - "assets"."id" = "move_history"."entityId" - ) - and "move_history"."pathType" = 'original' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE character varying`); - } -} - diff --git a/server/src/migrations/1741191762113-AssetAuditTable.ts b/server/src/migrations/1741191762113-AssetAuditTable.ts deleted file mode 100644 index c02408c384..0000000000 --- a/server/src/migrations/1741191762113-AssetAuditTable.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetAuditTable1741191762113 implements MigrationInterface { - name = 'AssetAuditTable1741191762113' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "assetId" uuid NOT NULL, "ownerId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_99bd5c015f81a641927a32b4212" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_asset_id" ON "assets_audit" ("assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_owner_id" ON "assets_audit" ("ownerId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_deleted_at" ON "assets_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION assets_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO assets_audit ("assetId", "ownerId") - SELECT "id", "ownerId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER assets_delete_audit`); - await queryRunner.query(`DROP FUNCTION assets_delete_audit`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_owner_id"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_asset_id"`); - await queryRunner.query(`DROP TABLE "assets_audit"`); - } -} diff --git a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts b/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts deleted file mode 100644 index 20215c1b59..0000000000 --- a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixAssetAndUserCascadeConditions1741280328985 implements MigrationInterface { - name = 'FixAssetAndUserCascadeConditions1741280328985'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION partners_delete_audit();`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit();`); - } -} diff --git a/server/src/migrations/1741281344519-AddExifUpdateId.ts b/server/src/migrations/1741281344519-AddExifUpdateId.ts deleted file mode 100644 index eb32836a1d..0000000000 --- a/server/src/migrations/1741281344519-AddExifUpdateId.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifUpdateId1741281344519 implements MigrationInterface { - name = 'AddExifUpdateId1741281344519'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "exif" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp()`, - ); - await queryRunner.query(`ALTER TABLE "exif" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId") `); - await queryRunner.query(` - create trigger asset_exif_updated_at - before update on exif - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_asset_exif_update_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updatedAt"`); - await queryRunner.query(`DROP TRIGGER asset_exif_updated_at on exif`); - } -} diff --git a/server/src/migrations/1743595393000-TableCleanup.ts b/server/src/migrations/1743595393000-TableCleanup.ts deleted file mode 100644 index adf9c65afa..0000000000 --- a/server/src/migrations/1743595393000-TableCleanup.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TableCleanup1743595393000 implements MigrationInterface { - name = 'TableCleanup1743595393000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE IF EXISTS "system_config"`); - await queryRunner.query(`DROP TABLE IF EXISTS "socket_io_attachments"`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1743611339000-GeodataCleanup.ts b/server/src/migrations/1743611339000-GeodataCleanup.ts deleted file mode 100644 index 0e25a1268e..0000000000 --- a/server/src/migrations/1743611339000-GeodataCleanup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataCleanup1743611339000 implements MigrationInterface { - name = 'GeodataCleanup1743611339000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_admin2_alternate_names" RENAME TO "idx_geodata_places_alternate_names"`, - ); - await queryRunner.query(`DROP TABLE IF EXISTS "geodata_places_tmp"`); - await queryRunner.query(`DROP TABLE IF EXISTS "naturalearth_countries_tmp"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_alternate_names" RENAME TO "idx_geodata_places_admin2_alternate_names"`, - ); - } -} diff --git a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts b/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts deleted file mode 100644 index 1ba4df01cd..0000000000 --- a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MakeFileMetadataNonNullable1744662638410 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM assets WHERE "fileCreatedAt" IS NULL OR "fileModifiedAt" IS NULL OR "localDateTime" IS NULL`, - ); - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" SET NOT NULL, - ALTER COLUMN "fileModifiedAt" SET NOT NULL, - ALTER COLUMN "localDateTime" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" DROP NOT NULL, - ALTER COLUMN "fileModifiedAt" DROP NOT NULL, - ALTER COLUMN "localDateTime" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts deleted file mode 100644 index db351d5bab..0000000000 --- a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddForeignKeyIndexes1744900200559 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`); - await queryRunner.query(`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`); - await queryRunner.query(`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`); - await queryRunner.query(`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`); - await queryRunner.query(`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`); - await queryRunner.query(`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`); - await queryRunner.query(`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_66fe3837414c5a9f1c33ca4934";`); - await queryRunner.query(`DROP INDEX "IDX_91704e101438fd0653f582426d";`); - await queryRunner.query(`DROP INDEX "IDX_c05079e542fd74de3b5ecb5c1c";`); - await queryRunner.query(`DROP INDEX "IDX_5527cc99f530a547093f9e577b";`); - await queryRunner.query(`DROP INDEX "IDX_2bbabe31656b6778c6b87b6102";`); - await queryRunner.query(`DROP INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c";`); - await queryRunner.query(`DROP INDEX "IDX_9f9590cc11561f1f48ff034ef9";`); - await queryRunner.query(`DROP INDEX "IDX_2c5ac0d6fb58b238fd2068de67";`); - await queryRunner.query(`DROP INDEX "IDX_16294b83fa8c0149719a1f631e";`); - await queryRunner.query(`DROP INDEX "IDX_9977c3c1de01c3d848039a6b90";`); - await queryRunner.query(`DROP INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20";`); - await queryRunner.query(`DROP INDEX "IDX_b22c53f35ef20c28c21637c85f";`); - await queryRunner.query(`DROP INDEX "IDX_05895aa505a670300d4816debc";`); - await queryRunner.query(`DROP INDEX "IDX_57de40bc620f456c7311aa3a1e";`); - await queryRunner.query(`DROP INDEX "IDX_d8ddd9d687816cc490432b3d4b";`); - await queryRunner.query(`DROP INDEX "IDX_d7e875c6c60e661723dbf372fd";`); - await queryRunner.query(`DROP INDEX "IDX_575842846f0c28fa5da46c99b1";`); - await queryRunner.query(`DROP INDEX "IDX_6c2e267ae764a9413b863a2934";`); - await queryRunner.query(`DROP INDEX "IDX_1af8519996fbfb3684b58df280";`); - await queryRunner.query(`DROP INDEX "IDX_3571467bcbe021f66e2bdce96e";`); - await queryRunner.query(`DROP INDEX "IDX_8091ea76b12338cb4428d33d78";`); - } -} diff --git a/server/src/migrations/1744910873956-AddMissingIndex.ts b/server/src/migrations/1744910873956-AddMissingIndex.ts deleted file mode 100644 index 38dd6f4958..0000000000 --- a/server/src/migrations/1744910873956-AddMissingIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMissingIndex1744910873956 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX IF NOT EXISTS "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord";`); - } -} diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index f334896ce1..e5d88339c8 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -3,7 +3,7 @@ import AsyncLock from 'async-lock'; import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { readdir } from 'node:fs/promises'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; import semver from 'semver'; import { EXTENSION_NAMES, @@ -23,10 +23,9 @@ import { DB } from 'src/schema'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; -import { DataSource, QueryRunner } from 'typeorm'; export let cachedVectorExtension: VectorExtension | undefined; -export async function getVectorExtension(runner: Kysely | QueryRunner): Promise { +export async function getVectorExtension(runner: Kysely): Promise { if (cachedVectorExtension) { return cachedVectorExtension; } @@ -36,14 +35,8 @@ export async function getVectorExtension(runner: Kysely | QueryRunner): Prom return cachedVectorExtension; } - let availableExtensions: { name: VectorExtension }[]; const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`; - if (runner instanceof Kysely) { - const { rows } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); - availableExtensions = rows; - } else { - availableExtensions = (await runner.query(query)) as { name: VectorExtension }[]; - } + const { rows: availableExtensions } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); const extensionNames = new Set(availableExtensions.map((row) => row.name)); cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext)); if (!cachedVectorExtension) { @@ -364,45 +357,9 @@ export class DatabaseRepository { return count; } - async runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise { - const { database } = this.configRepository.getEnv(); + async runMigrations(): Promise { + this.logger.debug('Running migrations'); - this.logger.log('Running migrations, this may take a while'); - - const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; - const { rows } = await tableExists.execute(this.db); - const hasTypeOrmMigrations = !!rows[0]?.result; - if (hasTypeOrmMigrations) { - // eslint-disable-next-line unicorn/prefer-module - const dist = resolve(`${__dirname}/..`); - - this.logger.debug('Running typeorm migrations'); - const dataSource = new DataSource({ - type: 'postgres', - entities: [], - subscribers: [], - migrations: [`${dist}/migrations` + '/*.{js,ts}'], - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - ...(database.config.connectionType === 'url' - ? { url: database.config.url } - : { - host: database.config.host, - port: database.config.port, - username: database.config.username, - password: database.config.password, - database: database.config.database, - }), - }); - await dataSource.initialize(); - await dataSource.runMigrations(options); - await dataSource.destroy(); - this.logger.debug('Finished running typeorm migrations'); - } - - this.logger.debug('Running kysely migrations'); const migrator = new Migrator({ db: this.db, migrationLockTableName: 'kysely_migrations_lock', @@ -429,11 +386,11 @@ export class DatabaseRepository { } if (error) { - this.logger.error(`Kysely migrations failed: ${error}`); + this.logger.error(`Migrations failed: ${error}`); throw error; } - this.logger.debug('Finished running kysely migrations'); + this.logger.debug('Finished running migrations'); } async migrateFilePaths(sourceFolder: string, targetFolder: string): Promise { diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index a85fd74c72..86d0bda7f8 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,10 +1,10 @@ import { BadRequestException } from '@nestjs/common'; +import { Readable } from 'node:stream'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; import { vitest } from 'vitest'; const downloadResponse: DownloadResponseDto = { diff --git a/server/test/utils.ts b/server/test/utils.ts index af6f2826f9..9f212578c0 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -4,7 +4,7 @@ import { Test } from '@nestjs/testing'; import { ClassConstructor } from 'class-transformer'; import { Kysely } from 'kysely'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; -import { Writable } from 'node:stream'; +import { Readable, Writable } from 'node:stream'; import { PNG } from 'pngjs'; import postgres from 'postgres'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; @@ -71,7 +71,6 @@ import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { ITelemetryRepositoryMock, newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; -import { Readable } from 'typeorm/platform/PlatformTools'; import { assert, Mock, Mocked, vitest } from 'vitest'; export type ControllerContext = { From 2f5d543ad91303e7e442d52b4b90282706f63771 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 17:33:24 -0400 Subject: [PATCH 133/169] fix: tweak error docs (#20417) --- docs/src/pages/errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index c5af7e2575..5f73162a61 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,7 +2,7 @@ ## TypeORM Upgrade -In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully started on this version, shut the system down and try the update again. We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. +In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to v1.137.0 (or above). We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. ## Inconsistent Media Location From 07ed060c3293077ea6b83dea1534d2e97ea4b658 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 21:55:21 -0500 Subject: [PATCH 134/169] feat: 3-2-1 backup onboarding card (#20374) * feat: 3-2-1 backup onboarding card * chore: format i18n * fix: lint * Update onboarding-backup.svelte * fix: e2e onboarding test --- e2e/src/web/specs/auth.e2e-spec.ts | 1 + i18n/en.json | 7 +++ .../onboarding-page/onboarding-backup.svelte | 62 +++++++++++++++++++ web/src/routes/auth/onboarding/+page.svelte | 10 ++- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 web/src/lib/components/onboarding-page/onboarding-backup.svelte diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index 0fde9a6ec6..173131ec5e 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -37,6 +37,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Server Privacy' }).click(); await page.getByRole('button', { name: 'User Privacy' }).click(); await page.getByRole('button', { name: 'Storage Template' }).click(); + await page.getByRole('button', { name: 'Backups' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success diff --git a/i18n/en.json b/i18n/en.json index 524466f4e3..49d24a2f84 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -44,6 +44,13 @@ "backup_database": "Create Database Dump", "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_onboarding_1_description": "offsite copy in the cloud or at another physical location.", + "backup_onboarding_2_description": "local copies on different devices. This includes the main files and a backup of those files locally.", + "backup_onboarding_3_description": "total copies of your data, including the original files. This includes 1 offsite copy and 2 local copies.", + "backup_onboarding_description": "A 3-2-1 backup strategy is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution.", + "backup_onboarding_footer": "For more information about backing up Immich, please refer to the documentation.", + "backup_onboarding_parts_title": "A 3-2-1 backup includes:", + "backup_onboarding_title": "Backups", "backup_settings": "Database Dump Settings", "backup_settings_description": "Manage database dump settings.", "cleared_jobs": "Cleared jobs for: {job}", diff --git a/web/src/lib/components/onboarding-page/onboarding-backup.svelte b/web/src/lib/components/onboarding-page/onboarding-backup.svelte new file mode 100644 index 0000000000..1ae176e1ad --- /dev/null +++ b/web/src/lib/components/onboarding-page/onboarding-backup.svelte @@ -0,0 +1,62 @@ + + +
+ + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+ +

+ +

+ + + + 3 + + + + 2 + + + + 1 + + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+
diff --git a/web/src/routes/auth/onboarding/+page.svelte b/web/src/routes/auth/onboarding/+page.svelte index 9c39c284b2..679920c971 100644 --- a/web/src/routes/auth/onboarding/+page.svelte +++ b/web/src/routes/auth/onboarding/+page.svelte @@ -8,12 +8,13 @@ import OnboardingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte'; import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte'; import OnboardingUserPrivacy from '$lib/components/onboarding-page/onboarding-user-privacy.svelte'; + import OnboardingBackup from '$lib/components/onboarding-page/onboarding-backup.svelte'; import { AppRoute, QueryParameter } from '$lib/constants'; import { OnboardingRole } from '$lib/models/onboarding-role'; import { retrieveServerConfig, retrieveSystemConfig, serverConfig } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk'; - import { mdiHarddisk, mdiIncognito, mdiThemeLightDark, mdiTranslate } from '@mdi/js'; + import { mdiCloudUpload, mdiHarddisk, mdiIncognito, mdiThemeLightDark, mdiTranslate } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; @@ -68,6 +69,13 @@ title: $t('admin.storage_template_settings'), icon: mdiHarddisk, }, + { + name: 'backup', + component: OnboardingBackup, + role: OnboardingRole.SERVER, + title: $t('admin.backup_onboarding_title'), + icon: mdiCloudUpload, + }, ]); let index = $state(0); From 268b411a6fa1edf08457d96d6f87c14d71fb1177 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:27:04 +0530 Subject: [PATCH 135/169] fix: sync is_favorite from native (#20412) * feat: sync is_favorite from native * handle favorite during upload * Update mobile/ios/Runner/Sync/MessagesImpl.swift Co-authored-by: Alex --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../src/main/kotlin/app/alextran/immich/sync/Messages.g.kt | 7 +++++-- .../kotlin/app/alextran/immich/sync/MessagesImplBase.kt | 4 ++++ mobile/ios/Runner/Sync/Messages.g.swift | 6 +++++- mobile/ios/Runner/Sync/MessagesImpl.swift | 3 ++- mobile/lib/domain/services/local_sync.service.dart | 1 + .../repositories/local_album.repository.dart | 1 + mobile/lib/platform/native_sync_api.g.dart | 6 +++++- mobile/lib/services/upload.service.dart | 5 ++++- mobile/pigeon/native_sync_api.dart | 5 +++-- 9 files changed, 30 insertions(+), 8 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index b5ef90310e..201d0a43e1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -88,7 +88,8 @@ data class PlatformAsset ( val width: Long? = null, val height: Long? = null, val durationInSeconds: Long, - val orientation: Long + val orientation: Long, + val isFavorite: Boolean ) { companion object { @@ -102,7 +103,8 @@ data class PlatformAsset ( val height = pigeonVar_list[6] as Long? val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation) + val isFavorite = pigeonVar_list[9] as Boolean + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) } } fun toList(): List { @@ -116,6 +118,7 @@ data class PlatformAsset ( height, durationInSeconds, orientation, + isFavorite, ) } override fun equals(other: Any?): Boolean { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 02cd54b8c3..d7073e7cfc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -42,6 +42,7 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.MediaColumns.HEIGHT, MediaStore.MediaColumns.DURATION, MediaStore.MediaColumns.ORIENTATION, + MediaStore.MediaColumns.IS_FAVORITE, ) const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 @@ -77,6 +78,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) + val favoriteColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -105,6 +107,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) + val isFavorite = c.getInt(favoriteColumn) != 0; val asset = PlatformAsset( id, @@ -116,6 +119,7 @@ open class NativeSyncApiImplBase(context: Context) { height, duration, orientation.toLong(), + isFavorite, ) yield(AssetResult.ValidAsset(asset, bucketId)) } diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index e629604d6a..b7f4293836 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -139,6 +139,7 @@ struct PlatformAsset: Hashable { var height: Int64? = nil var durationInSeconds: Int64 var orientation: Int64 + var isFavorite: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -152,6 +153,7 @@ struct PlatformAsset: Hashable { let height: Int64? = nilOrValue(pigeonVar_list[6]) let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 + let isFavorite = pigeonVar_list[9] as! Bool return PlatformAsset( id: id, @@ -162,7 +164,8 @@ struct PlatformAsset: Hashable { width: width, height: height, durationInSeconds: durationInSeconds, - orientation: orientation + orientation: orientation, + isFavorite: isFavorite ) } func toList() -> [Any?] { @@ -176,6 +179,7 @@ struct PlatformAsset: Hashable { height, durationInSeconds, orientation, + isFavorite, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index 459e29fa5a..b8d97b0a82 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -28,7 +28,8 @@ extension PHAsset { width: Int64(pixelWidth), height: Int64(pixelHeight), durationInSeconds: Int64(duration), - orientation: 0 + orientation: 0, + isFavorite: isFavorite ) } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index cb1bb40619..13ebecfd46 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -312,6 +312,7 @@ extension on Iterable { height: e.height, durationInSeconds: e.durationInSeconds, orientation: e.orientation, + isFavorite: e.isFavorite, ), ).toList(); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index feb25925f8..869d8f0dc8 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -236,6 +236,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { id: asset.id, orientation: Value(asset.orientation), checksum: const Value(null), + isFavorite: Value(asset.isFavorite), ); batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 67a320a96d..3cbd08cd68 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -40,6 +40,7 @@ class PlatformAsset { this.height, required this.durationInSeconds, required this.orientation, + required this.isFavorite, }); String id; @@ -60,8 +61,10 @@ class PlatformAsset { int orientation; + bool isFavorite; + List _toList() { - return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite]; } Object encode() { @@ -80,6 +83,7 @@ class PlatformAsset { height: result[6] as int?, durationInSeconds: result[7]! as int, orientation: result[8]! as int, + isFavorite: result[9]! as bool, ); } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index dca5c02feb..c41d2b2e5f 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -247,6 +247,7 @@ class UploadService { metadata: metadata, group: group, priority: priority, + isFavorite: asset.isFavorite, ); } @@ -270,6 +271,7 @@ class UploadService { fields: fields, group: kBackupLivePhotoGroup, priority: 0, // Highest priority to get upload immediately + isFavorite: asset.isFavorite, ); } @@ -281,6 +283,7 @@ class UploadService { String? deviceAssetId, String? metadata, int? priority, + bool? isFavorite, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); @@ -297,7 +300,7 @@ class UploadService { 'deviceId': deviceId, 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), - 'isFavorite': 'false', + 'isFavorite': isFavorite?.toString() ?? 'false', 'duration': '0', if (fields != null) ...fields, }; diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 4f14b7a0b9..e84c814c3d 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -5,8 +5,7 @@ import 'package:pigeon/pigeon.dart'; dartOut: 'lib/platform/native_sync_api.g.dart', swiftOut: 'ios/Runner/Sync/Messages.g.swift', swiftOptions: SwiftOptions(), - kotlinOut: - 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', kotlinOptions: KotlinOptions(package: 'app.alextran.immich.sync'), dartOptions: DartOptions(), dartPackageName: 'immich_mobile', @@ -24,6 +23,7 @@ class PlatformAsset { final int? height; final int durationInSeconds; final int orientation; + final bool isFavorite; const PlatformAsset({ required this.id, @@ -35,6 +35,7 @@ class PlatformAsset { this.height, this.durationInSeconds = 0, this.orientation = 0, + this.isFavorite = false, }); } From 29f16c6a4718ab975960f2de732e85e54656300e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 29 Jul 2025 22:07:53 -0500 Subject: [PATCH 136/169] feat: people page/sheet/detail (#20309) --- i18n/en.json | 2 + mobile/lib/domain/models/person.model.dart | 10 +- .../lib/domain/services/people.service.dart | 30 + .../lib/domain/services/timeline.service.dart | 3 + .../repositories/asset_face.repository.dart | 30 - .../repositories/people.repository.dart | 67 +++ .../repositories/person.repository.dart | 34 -- .../repositories/timeline.repository.dart | 83 +++ .../library/partner/drift_partner.page.dart | 2 +- .../pages/drift_library.page.dart | 8 +- .../pages/drift_people_collection.page.dart | 130 ++++ .../presentation/pages/drift_person.page.dart | 97 +++ .../asset_viewer/asset_viewer.page.dart | 6 +- .../asset_viewer/bottom_sheet.widget.dart | 4 +- ...art => sheet_location_details.widget.dart} | 0 .../sheet_people_details.widget.dart | 175 ++++++ .../partner_user_avatar.widget.dart | 0 .../person_edit_birthday_modal.widget.dart | 116 ++++ .../people/person_edit_name_modal.widget.dart | 82 +++ .../people/person_option_sheet.widget.dart | 36 ++ .../widgets/timeline/fixed/segment.model.dart | 13 +- .../infrastructure/asset_face.provider.dart | 7 - .../infrastructure/people.provider.dart | 24 + .../infrastructure/person.provider.dart | 5 - mobile/lib/providers/routes.provider.dart | 2 + .../repositories/person_api.repository.dart | 4 +- .../lib/routing/app_navigation_observer.dart | 1 + mobile/lib/routing/router.dart | 6 + mobile/lib/routing/router.gr.dart | 53 ++ mobile/lib/utils/people.utils.dart | 54 ++ .../widgets/common/person_sliver_app_bar.dart | 562 ++++++++++++++++++ .../photo_view/photo_view_gallery.dart | 4 +- mobile/pubspec.lock | 8 + mobile/pubspec.yaml | 1 + 34 files changed, 1562 insertions(+), 97 deletions(-) create mode 100644 mobile/lib/domain/services/people.service.dart delete mode 100644 mobile/lib/infrastructure/repositories/asset_face.repository.dart create mode 100644 mobile/lib/infrastructure/repositories/people.repository.dart delete mode 100644 mobile/lib/infrastructure/repositories/person.repository.dart create mode 100644 mobile/lib/presentation/pages/drift_people_collection.page.dart create mode 100644 mobile/lib/presentation/pages/drift_person.page.dart rename mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/{location_details.widget.dart => sheet_location_details.widget.dart} (100%) create mode 100644 mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart rename mobile/lib/presentation/widgets/{ => people}/partner_user_avatar.widget.dart (100%) create mode 100644 mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart create mode 100644 mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart create mode 100644 mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart delete mode 100644 mobile/lib/providers/infrastructure/asset_face.provider.dart create mode 100644 mobile/lib/providers/infrastructure/people.provider.dart delete mode 100644 mobile/lib/providers/infrastructure/person.provider.dart create mode 100644 mobile/lib/utils/people.utils.dart create mode 100644 mobile/lib/widgets/common/person_sliver_app_bar.dart diff --git a/i18n/en.json b/i18n/en.json index 49d24a2f84..c445110996 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,6 +14,7 @@ "add_a_location": "Add a location", "add_a_name": "Add a name", "add_a_title": "Add a title", + "add_birthday": "Add a birthday", "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Add exclusion pattern", "add_import_path": "Add import path", @@ -828,6 +829,7 @@ "edit": "Edit", "edit_album": "Edit album", "edit_avatar": "Edit avatar", + "edit_birthday": "Edit Birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", "edit_description": "Edit description", diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index 784bb564fe..7559720c45 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -91,7 +91,7 @@ class PersonDto { } // Model for a person stored in the server -class Person { +class DriftPerson { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -103,7 +103,7 @@ class Person { final String? color; final DateTime? birthDate; - const Person({ + const DriftPerson({ required this.id, required this.createdAt, required this.updatedAt, @@ -116,7 +116,7 @@ class Person { this.birthDate, }); - Person copyWith({ + DriftPerson copyWith({ String? id, DateTime? createdAt, DateTime? updatedAt, @@ -128,7 +128,7 @@ class Person { String? color, DateTime? birthDate, }) { - return Person( + return DriftPerson( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, @@ -159,7 +159,7 @@ class Person { } @override - bool operator ==(covariant Person other) { + bool operator ==(covariant DriftPerson other) { if (identical(this, other)) return true; return other.id == id && diff --git a/mobile/lib/domain/services/people.service.dart b/mobile/lib/domain/services/people.service.dart new file mode 100644 index 0000000000..d45f710d7b --- /dev/null +++ b/mobile/lib/domain/services/people.service.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +class DriftPeopleService { + final DriftPeopleRepository _repository; + final PersonApiRepository _personApiRepository; + + const DriftPeopleService(this._repository, this._personApiRepository); + + Future> getAssetPeople(String assetId) { + return _repository.getAssetPeople(assetId); + } + + Future> getAllPeople() { + return _repository.getAllPeople(); + } + + Future updateName(String personId, String name) async { + await _personApiRepository.update(personId, name: name); + return _repository.updateName(personId, name); + } + + Future updateBrithday(String personId, DateTime birthday) async { + await _personApiRepository.update(personId, birthday: birthday); + return _repository.updateBirthday(personId, birthday); + } +} diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 7c22fb786d..9fa4106d17 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -53,6 +53,9 @@ class TimelineFactory { TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy)); + TimelineService person(String userId, String personId) => + TimelineService(_timelineRepository.person(userId, personId, groupBy)); + TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); } diff --git a/mobile/lib/infrastructure/repositories/asset_face.repository.dart b/mobile/lib/infrastructure/repositories/asset_face.repository.dart deleted file mode 100644 index 7b3b97058b..0000000000 --- a/mobile/lib/infrastructure/repositories/asset_face.repository.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/asset_face.model.dart'; -import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class DriftAssetFaceRepository extends DriftDatabaseRepository { - final Drift _db; - const DriftAssetFaceRepository(this._db) : super(_db); - - Future> getAll() { - return _db.assetFaceEntity.select().map((assetFace) => assetFace.toDto()).get(); - } -} - -extension on AssetFaceEntityData { - AssetFace toDto() { - return AssetFace( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart new file mode 100644 index 0000000000..9c6ed74636 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPeopleRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPeopleRepository(this._db) : super(_db); + + Future> getAssetPeople(String assetId) async { + final query = _db.select(_db.assetFaceEntity).join([ + innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)), + ])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false)); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future> getAllPeople() async { + final query = + _db.select(_db.personEntity).join([ + leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)), + ]) + ..where(_db.personEntity.isHidden.equals(false)) + ..groupBy([_db.personEntity.id]) + ..orderBy([ + OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc), + OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc), + ]); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future updateName(String personId, String name) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now()))); + } + + Future updateBirthday(String personId, DateTime birthday) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now()))); + } +} + +extension on PersonEntityData { + DriftPerson toDto() { + return DriftPerson( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart deleted file mode 100644 index 045fab4942..0000000000 --- a/mobile/lib/infrastructure/repositories/person.repository.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/person.model.dart'; -import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class DriftPersonRepository extends DriftDatabaseRepository { - final Drift _db; - const DriftPersonRepository(this._db) : super(_db); - - Future> getAll(String userId) { - final query = _db.personEntity.select()..where((e) => e.ownerId.equals(userId)); - - return query.map((person) { - return person.toDto(); - }).get(); - } -} - -extension on PersonEntityData { - Person toDto() { - return Person( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index dcd10faa64..663db9b82f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -292,6 +292,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), ); + TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), + assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count), + ); + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place @@ -344,6 +349,84 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } + Stream> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + if (groupBy == GroupAssetsBy.none) { + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.id.count()]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ); + + return query.map((row) { + final count = row.read(_db.remoteAssetEntity.id.count())!; + return _generateBuckets(count); + }).watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getPersonBucketAssets( + String userId, + String personId, { + required int offset, + required int count, + }) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index 171fe0ea0d..834e55ffd4 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 28fa6c6a16..af7014bbdc 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -6,10 +6,10 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; -import 'package:immich_mobile/providers/search/people.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -144,7 +144,7 @@ class _PeopleCollectionCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final people = ref.watch(getAllPeopleProvider); + final people = ref.watch(driftGetAllPeopleProvider); return LayoutBuilder( builder: (context, constraints) { @@ -153,7 +153,7 @@ class _PeopleCollectionCard extends ConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PeopleCollectionRoute()), + onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/mobile/lib/presentation/pages/drift_people_collection.page.dart b/mobile/lib/presentation/pages/drift_people_collection.page.dart new file mode 100644 index 0000000000..ca4e20aad0 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_people_collection.page.dart @@ -0,0 +1,130 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftPeopleCollectionPage extends ConsumerStatefulWidget { + const DriftPeopleCollectionPage({super.key}); + + @override + ConsumerState createState() => _DriftPeopleCollectionPageState(); +} + +class _DriftPeopleCollectionPageState extends ConsumerState { + final FocusNode _formFocus = FocusNode(); + String? _search; + + @override + void dispose() { + _formFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final people = ref.watch(driftGetAllPeopleProvider); + final headers = ApiService.getRequestHeaders(); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final isPortrait = context.orientation == Orientation.portrait; + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: _search == null, + title: _search != null + ? SearchField( + focusNode: _formFocus, + onTapOutside: (_) => _formFocus.unfocus(), + onChanged: (value) => setState(() => _search = value), + filled: true, + hintText: 'filter_people'.tr(), + autofocus: true, + ) + : Text('people'.tr()), + actions: [ + IconButton( + icon: Icon(_search != null ? Icons.close : Icons.search), + onPressed: () { + setState(() => _search = _search == null ? '' : null); + }, + ), + ], + ), + body: SafeArea( + child: people.when( + data: (people) { + if (_search != null) { + people = people.where((person) { + return person.name.toLowerCase().contains(_search!.toLowerCase()); + }).toList(); + } + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isTablet ? 6 : 3, + childAspectRatio: 0.85, + mainAxisSpacing: isPortrait && isTablet ? 36 : 0, + ), + padding: const EdgeInsets.symmetric(vertical: 32), + itemCount: people.length, + itemBuilder: (context, index) { + final person = people[index]; + + return Column( + children: [ + GestureDetector( + onTap: () { + context.pushRoute(DriftPersonRoute(person: person)); + }, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: isTablet ? 100 / 2 : 96 / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () => showNameEditModal(context, person), + child: person.name.isEmpty + ? Text( + 'add_a_name'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.primary, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + person.name, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), + ), + ), + ), + ], + ); + }, + ); + }, + error: (error, stack) => const Text("error"), + loading: () => const Center(child: CircularProgressIndicator()), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_person.page.dart b/mobile/lib/presentation/pages/drift_person.page.dart new file mode 100644 index 0000000000..f3146505ae --- /dev/null +++ b/mobile/lib/presentation/pages/drift_person.page.dart @@ -0,0 +1,97 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_option_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart'; + +@RoutePage() +class DriftPersonPage extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonPage({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonPageState(); +} + +class _DriftPersonPageState extends ConsumerState { + late DriftPerson _person; + + @override + initState() { + super.initState(); + _person = widget.person; + } + + Future handleEditName(BuildContext context) async { + final newName = await showNameEditModal(context, _person); + + if (newName != null && newName.isNotEmpty) { + setState(() { + _person = _person.copyWith(name: newName); + }); + } + } + + Future handleEditBirthday(BuildContext context) async { + final birthday = await showBirthdayEditModal(context, _person); + + if (birthday != null) { + setState(() { + _person = _person.copyWith(birthDate: birthday); + }); + } + } + + void showOptionSheet(BuildContext context) { + showModalBottomSheet( + context: context, + backgroundColor: context.colorScheme.surface, + isScrollControlled: false, + builder: (context) { + return PersonOptionSheet( + onEditName: () async { + await handleEditName(context); + context.pop(); + }, + onEditBirthday: () async { + await handleEditBirthday(context); + context.pop(); + }, + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to view person timeline'); + } + + final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: Timeline( + appBar: PersonSliverAppBar( + person: _person, + onNameTap: () => handleEditName(context), + onBirthdayTap: () => handleEditBirthday(context), + onShowOptions: () => showOptionSheet(context), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index c6b2360c9d..2fa54ad65d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -218,7 +218,7 @@ class _AssetViewerState extends ConsumerState { void _onPageBuild(PhotoViewControllerBase controller) { viewController ??= controller; - if (showingBottomSheet) { + if (showingBottomSheet && bottomSheetController.isAttached) { final verticalOffset = (context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent); controller.position = Offset(0, -verticalOffset); @@ -463,7 +463,9 @@ class _AssetViewerState extends ConsumerState { } void _snapBottomSheet() { - if (bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) { + if (!bottomSheetController.isAttached || + bottomSheetController.size > _kBottomSheetSnapExtent || + bottomSheetController.size < 0.4) { return; } isSnapping = true; diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index f27261a357..17b4cdb214 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -16,7 +16,8 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; @@ -150,6 +151,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + const SheetPeopleDetails(), const SheetLocationDetails(), // Details header _SheetTile( diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart similarity index 100% rename from mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart rename to mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart new file mode 100644 index 0000000000..4cfd95b25c --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -0,0 +1,175 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class SheetPeopleDetails extends ConsumerStatefulWidget { + const SheetPeopleDetails({super.key}); + + @override + ConsumerState createState() => _SheetPeopleDetailsState(); +} + +class _SheetPeopleDetailsState extends ConsumerState { + @override + Widget build(BuildContext context) { + final asset = ref.watch(currentAssetNotifier); + if (asset is! RemoteAsset) { + return const SizedBox.shrink(); + } + + final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id)); + + Future showNameEditModal(DriftPerson person) async { + await showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); + + ref.invalidate(driftPeopleAssetProvider(asset.id)); + } + + return peopleFuture.when( + data: (people) { + return AnimatedCrossFade( + firstChild: const SizedBox.shrink(), + secondChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: Text( + "people".t(context: context).toUpperCase(), + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 150, + child: ListView( + padding: const EdgeInsets.only(left: 16.0), + scrollDirection: Axis.horizontal, + children: [ + for (final person in people) + _PeopleAvatar( + person: person, + assetFileCreatedAt: asset.createdAt, + onTap: () { + final previousRouteData = ref.read(previousRouteDataProvider); + final previousRouteArgs = previousRouteData?.arguments; + + // Prevent circular navigation + if (previousRouteArgs is DriftPersonRouteArgs && previousRouteArgs.person.id == person.id) { + context.back(); + return; + } + context.back(); + context.pushRoute(DriftPersonRoute(person: person)); + }, + onNameTap: () => showNameEditModal(person), + ), + ], + ), + ), + ], + ), + crossFadeState: people.isEmpty ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: Durations.short4, + ); + }, + error: (error, stack) => Text("error_loading_people".t(context: context), style: context.textTheme.bodyMedium), + loading: () => const SizedBox.shrink(), + ); + } +} + +class _PeopleAvatar extends StatelessWidget { + final DriftPerson person; + final DateTime assetFileCreatedAt; + final VoidCallback? onTap; + final VoidCallback? onNameTap; + final double imageSize = 96; + + const _PeopleAvatar({required this.person, required this.assetFileCreatedAt, this.onTap, this.onNameTap}); + + @override + Widget build(BuildContext context) { + final headers = ApiService.getRequestHeaders(); + + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 96), + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: onTap, + child: SizedBox( + height: imageSize, + child: Material( + shape: CircleBorder(side: BorderSide(color: context.primaryColor.withAlpha(50), width: 1.0)), + shadowColor: context.colorScheme.shadow, + elevation: 3, + child: CircleAvatar( + maxRadius: imageSize / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + ), + const SizedBox(height: 4), + if (person.name.isEmpty) + GestureDetector( + onTap: () => onNameTap?.call(), + child: Text( + "add_a_name".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ) + else + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + person.name, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelLarge, + maxLines: 1, + ), + if (person.birthDate != null) + Text( + formatAge(person.birthDate!, assetFileCreatedAt), + textAlign: TextAlign.center, + style: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(175), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart similarity index 100% rename from mobile/lib/presentation/widgets/partner_user_avatar.widget.dart rename to mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart diff --git a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart new file mode 100644 index 0000000000..8d05244617 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart @@ -0,0 +1,116 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:scroll_date_picker/scroll_date_picker.dart'; + +class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonBirthdayEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late DateTime _selectedDate; + + @override + void initState() { + super.initState(); + _selectedDate = widget.person.birthDate ?? DateTime.now(); + } + + void saveBirthday() async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateBrithday(widget.person.id, _selectedDate); + + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(_selectedDate); + } + } catch (error) { + debugPrint('Error updating birthday: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + "edit_birthday".t(context: context), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + content: SizedBox( + width: double.maxFinite, + height: 300, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + + child: ScrollDatePicker( + options: DatePickerOptions( + backgroundColor: context.colorScheme.surfaceContainerHigh, + itemExtent: 50, + diameterRatio: 5, + ), + scrollViewOptions: DatePickerScrollViewOptions( + day: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + month: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + year: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + selectedDate: _selectedDate, + locale: context.locale, + minimumDate: DateTime(1800, 1, 1), + onDateTimeChanged: (DateTime value) { + setState(() { + _selectedDate = value; + }); + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => saveBirthday(), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart new file mode 100644 index 0000000000..46fd683b81 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DriftPersonNameEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonNameEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late TextEditingController _formController; + + @override + void initState() { + super.initState(); + _formController = TextEditingController(text: widget.person.name); + } + + void onEdit(String personId, String newName) async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateName(personId, newName); + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(newName); + } + } catch (error) { + debugPrint('Error updating name: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("edit_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + content: SingleChildScrollView( + child: TextFormField( + controller: _formController, + textCapitalization: TextCapitalization.words, + autofocus: true, + decoration: InputDecoration(hintText: 'name'.tr(), border: const OutlineInputBorder()), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => onEdit(widget.person.id, _formController.text), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart new file mode 100644 index 0000000000..6d46b37761 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class PersonOptionSheet extends ConsumerWidget { + const PersonOptionSheet({super.key, this.onEditName, this.onEditBirthday}); + + final VoidCallback? onEditName; + final VoidCallback? onEditBirthday; + + @override + Widget build(BuildContext context, WidgetRef ref) { + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); + + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: ListView( + shrinkWrap: true, + children: [ + ListTile( + leading: const Icon(Icons.edit), + title: Text('edit_name'.t(context: context), style: textStyle), + onTap: onEditName, + ), + ListTile( + leading: const Icon(Icons.cake), + title: Text('edit_birthday'.t(context: context), style: textStyle), + onTap: onEditBirthday, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index ea5f55b35e..a5f0c19eb8 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; @@ -99,7 +100,7 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount)); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount), timelineService); } return FutureBuilder>( @@ -108,7 +109,7 @@ class _FixedSegmentRow extends ConsumerWidget { if (snapshot.connectionState != ConnectionState.done) { return _buildPlaceholder(context); } - return _buildAssetRow(context, snapshot.requireData); + return _buildAssetRow(context, snapshot.requireData, timelineService); }, ); } @@ -117,14 +118,18 @@ class _FixedSegmentRow extends ConsumerWidget { return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } - Widget _buildAssetRow(BuildContext context, List assets) { + Widget _buildAssetRow(BuildContext context, List assets, TimelineService timelineService) { return FixedTimelineRow( dimension: tileHeight, spacing: spacing, textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget(key: ValueKey(assets[i].heroTag), asset: assets[i], assetIndex: assetIndex + i), + _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ], ); } diff --git a/mobile/lib/providers/infrastructure/asset_face.provider.dart b/mobile/lib/providers/infrastructure/asset_face.provider.dart deleted file mode 100644 index 386609ba94..0000000000 --- a/mobile/lib/providers/infrastructure/asset_face.provider.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/infrastructure/repositories/asset_face.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; - -final driftAssetFaceProvider = Provider( - (ref) => DriftAssetFaceRepository(ref.watch(driftProvider)), -); diff --git a/mobile/lib/providers/infrastructure/people.provider.dart b/mobile/lib/providers/infrastructure/people.provider.dart new file mode 100644 index 0000000000..94a1b2447f --- /dev/null +++ b/mobile/lib/providers/infrastructure/people.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/services/people.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +final driftPeopleRepositoryProvider = Provider( + (ref) => DriftPeopleRepository(ref.watch(driftProvider)), +); + +final driftPeopleServiceProvider = Provider( + (ref) => DriftPeopleService(ref.watch(driftPeopleRepositoryProvider), ref.watch(personApiRepositoryProvider)), +); + +final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAssetPeople(assetId); +}); + +final driftGetAllPeopleProvider = FutureProvider>((ref) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAllPeople(); +}); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart deleted file mode 100644 index ac8a457e3a..0000000000 --- a/mobile/lib/providers/infrastructure/person.provider.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; - -final driftPersonProvider = Provider((ref) => DriftPersonRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart index 52adabe233..c51f67bc0e 100644 --- a/mobile/lib/providers/routes.provider.dart +++ b/mobile/lib/providers/routes.provider.dart @@ -1,5 +1,7 @@ +import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; final inLockedViewProvider = StateProvider((ref) => false); final currentRouteNameProvider = StateProvider((ref) => null); final previousRouteNameProvider = StateProvider((ref) => null); +final previousRouteDataProvider = StateProvider((ref) => null); diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 545ad06741..04e4bd2a2c 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -16,8 +16,8 @@ class PersonApiRepository extends ApiRepository { return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name}) async { - final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name))); + Future update(String id, {String? name, DateTime? birthday}) async { + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name, birthDate: birthday))); return _toPerson(dto); } diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 20973fabd1..26ec017b9a 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -22,6 +22,7 @@ class AppNavigationObserver extends AutoRouterObserver { Future(() { ref.read(currentRouteNameProvider.notifier).state = route.settings.name; ref.read(previousRouteNameProvider.notifier).state = previousRoute?.settings.name; + ref.read(previousRouteDataProvider.notifier).state = previousRoute?.settings; }); } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index f547db65d6..54d4d080d4 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -6,6 +6,7 @@ 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/log.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -88,6 +89,8 @@ import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart'; import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_people_collection.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_person.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart'; @@ -323,6 +326,9 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), + + AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index f67d1e2623..63e8c6ecfe 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -966,6 +966,59 @@ class DriftPartnerRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftPeopleCollectionPage] +class DriftPeopleCollectionRoute extends PageRouteInfo { + const DriftPeopleCollectionRoute({List? children}) + : super(DriftPeopleCollectionRoute.name, initialChildren: children); + + static const String name = 'DriftPeopleCollectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPeopleCollectionPage(); + }, + ); +} + +/// generated route for +/// [DriftPersonPage] +class DriftPersonRoute extends PageRouteInfo { + DriftPersonRoute({ + Key? key, + required DriftPerson person, + List? children, + }) : super( + DriftPersonRoute.name, + args: DriftPersonRouteArgs(key: key, person: person), + initialChildren: children, + ); + + static const String name = 'DriftPersonRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftPersonPage(key: args.key, person: args.person); + }, + ); +} + +class DriftPersonRouteArgs { + const DriftPersonRouteArgs({this.key, required this.person}); + + final Key? key; + + final DriftPerson person; + + @override + String toString() { + return 'DriftPersonRouteArgs{key: $key, person: $person}'; + } +} + /// generated route for /// [DriftPlaceDetailPage] class DriftPlaceDetailRoute extends PageRouteInfo { diff --git a/mobile/lib/utils/people.utils.dart b/mobile/lib/utils/people.utils.dart new file mode 100644 index 0000000000..1b0a81a8cc --- /dev/null +++ b/mobile/lib/utils/people.utils.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; + +String formatAge(DateTime birthDate, DateTime referenceDate) { + int ageInYears = _calculateAge(birthDate, referenceDate); + int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); + + if (ageInMonths <= 11) { + return "exif_bottom_sheet_person_age_months".t(args: {'months': ageInMonths.toString()}); + } else if (ageInMonths > 12 && ageInMonths <= 23) { + return "exif_bottom_sheet_person_age_year_months".t(args: {'months': (ageInMonths - 12).toString()}); + } else { + return "exif_bottom_sheet_person_age_years".t(args: {'years': ageInYears.toString()}); + } +} + +int _calculateAge(DateTime birthDate, DateTime referenceDate) { + int age = referenceDate.year - birthDate.year; + if (referenceDate.month < birthDate.month || + (referenceDate.month == birthDate.month && referenceDate.day < birthDate.day)) { + age--; + } + return age; +} + +int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { + return (referenceDate.year - birthDate.year) * 12 + + referenceDate.month - + birthDate.month - + (referenceDate.day < birthDate.day ? 1 : 0); +} + +Future showNameEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); +} + +Future showBirthdayEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonBirthdayEditForm(person: person); + }, + ); +} diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart new file mode 100644 index 0000000000..1cc117139d --- /dev/null +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -0,0 +1,562 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class PersonSliverAppBar extends ConsumerStatefulWidget { + const PersonSliverAppBar({ + super.key, + required this.person, + required this.onNameTap, + required this.onShowOptions, + required this.onBirthdayTap, + }); + + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + final VoidCallback onShowOptions; + + @override + ConsumerState createState() => _MesmerizingSliverAppBarState(); +} + +class _MesmerizingSliverAppBarState extends ConsumerState { + double _scrollProgress = 0.0; + + double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { + if (settings?.maxExtent == null || settings?.minExtent == null) { + return 1.0; + } + + final deltaExtent = settings!.maxExtent - settings.minExtent; + if (deltaExtent <= 0.0) { + return 1.0; + } + + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); + } + + @override + Widget build(BuildContext context) { + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); + List actionIconShadows = [ + if (_scrollProgress < 0.95) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + else + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ]; + + return isMultiSelectEnabled + ? SliverToBoxAdapter( + child: switch (_scrollProgress) { + < 0.8 => const SizedBox(height: 120), + _ => const SizedBox(height: 352), + }, + ) + : SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ], + ), + onPressed: () { + context.pop(); + }, + ), + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); + + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); + + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.person.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: _ExpandedBackground( + scrollProgress: scrollProgress, + person: widget.person, + onNameTap: widget.onNameTap, + onBirthdayTap: widget.onBirthdayTap, + ), + ); + }, + ), + ); + } +} + +class _ExpandedBackground extends ConsumerStatefulWidget { + final double scrollProgress; + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + + const _ExpandedBackground({ + required this.scrollProgress, + required this.person, + required this.onNameTap, + required this.onBirthdayTap, + }); + + @override + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); +} + +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { + late AnimationController _slideController; + late Animation _slideAnimation; + + @override + void initState() { + super.initState(); + + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); + + _slideAnimation = Tween( + begin: const Offset(0, 1.5), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); + + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted) { + _slideController.forward(); + } + }); + } + + @override + void dispose() { + _slideController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final timelineService = ref.watch(timelineServiceProvider); + + return Stack( + fit: StackFit.expand, + children: [ + Transform.translate( + offset: Offset(0, widget.scrollProgress * 50), + child: Transform.scale( + scale: 1.4 - (widget.scrollProgress * 0.2), + child: _RandomAssetBackground(timelineService: timelineService), + ), + ), + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.05), + Colors.transparent, + Colors.black.withValues(alpha: 0.3), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), + ], + stops: const [0.0, 0.15, 0.55, 1.0], + ), + ), + ), + ), + ), + Positioned( + bottom: 16, + left: 16, + right: 16, + child: SlideTransition( + position: _slideAnimation, + child: Row( + children: [ + SizedBox( + height: 84, + width: 84, + child: Material( + shape: const CircleBorder(side: BorderSide(color: Colors.grey, width: 1.0)), + elevation: 3, + child: CircleAvatar( + maxRadius: 84 / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(widget.person.id), + headers: ApiService.getRequestHeaders(), + ), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => widget.onNameTap.call(), + child: SizedBox( + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: widget.person.name.isNotEmpty + ? Text( + widget.person.name, + maxLines: 1, + style: const TextStyle( + color: Colors.white, + fontSize: 36, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], + ), + ) + : Text( + 'add_a_name'.tr(), + style: context.textTheme.titleLarge?.copyWith( + color: Colors.grey[400], + fontSize: 36, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), + const SizedBox(height: 8), + GestureDetector( + onTap: widget.onBirthdayTap, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.cake_rounded, color: Colors.white, size: 14), + const SizedBox(width: 4), + + if (widget.person.birthDate != null) + Text( + "${DateFormat.yMMMd(context.locale.toString()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})", + style: context.textTheme.labelLarge?.copyWith( + color: Colors.white, + height: 1.2, + fontSize: 14, + ), + ) + else + Text( + 'add_birthday'.tr(), + style: context.textTheme.labelLarge?.copyWith( + color: Colors.grey[400], + height: 1.2, + fontSize: 14, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _ItemCountText extends ConsumerStatefulWidget { + const _ItemCountText(); + + @override + ConsumerState<_ItemCountText> createState() => _ItemCountTextState(); +} + +class _ItemCountTextState extends ConsumerState<_ItemCountText> { + StreamSubscription? _reloadSubscription; + + @override + void initState() { + super.initState(); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); + } + + @override + void dispose() { + _reloadSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); + + return Text( + 'items_count'.t(context: context, args: {"count": assetCount}), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], + ), + ); + } +} + +class _RandomAssetBackground extends StatefulWidget { + final TimelineService timelineService; + + const _RandomAssetBackground({required this.timelineService}); + + @override + State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); +} + +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { + late AnimationController _zoomController; + late AnimationController _crossFadeController; + late Animation _zoomAnimation; + late Animation _panAnimation; + late Animation _crossFadeAnimation; + BaseAsset? _currentAsset; + BaseAsset? _nextAsset; + bool _isZoomingIn = true; + + @override + void initState() { + super.initState(); + + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + + _zoomAnimation = Tween( + begin: 1.0, + end: 1.2, + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _panAnimation = Tween( + begin: Offset.zero, + end: const Offset(0.5, -0.5), + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _crossFadeAnimation = Tween( + begin: 0.0, + end: 1.0, + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); + + Future.delayed(Durations.medium1, () => _loadFirstAsset()); + } + + @override + void dispose() { + _zoomController.dispose(); + _crossFadeController.dispose(); + super.dispose(); + } + + void _startAnimationCycle() { + if (_isZoomingIn) { + _zoomController.forward().then((_) { + _loadNextAsset(); + }); + } else { + _zoomController.reverse().then((_) { + _loadNextAsset(); + }); + } + } + + Future _loadFirstAsset() async { + if (!mounted) { + return; + } + + if (widget.timelineService.totalAssets == 0) { + setState(() { + _currentAsset = null; + }); + + return; + } + + setState(() { + _currentAsset = widget.timelineService.getRandomAsset(); + }); + + await _crossFadeController.forward(); + + if (_zoomController.status == AnimationStatus.dismissed) { + if (_isZoomingIn) { + _zoomController.reset(); + } else { + _zoomController.value = 1.0; + } + _startAnimationCycle(); + } + } + + Future _loadNextAsset() async { + if (!mounted) { + return; + } + + try { + if (widget.timelineService.totalAssets > 1) { + // Load next asset while keeping current one visible + final nextAsset = widget.timelineService.getRandomAsset(); + + setState(() { + _nextAsset = nextAsset; + }); + + await _crossFadeController.reverse(); + setState(() { + _currentAsset = _nextAsset; + _nextAsset = null; + }); + + _crossFadeController.value = 1.0; + + _isZoomingIn = !_isZoomingIn; + + _startAnimationCycle(); + } + } catch (e) { + _zoomController.reset(); + _startAnimationCycle(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.timelineService.totalAssets == 0) { + return const SizedBox.shrink(); + } + + return AnimatedBuilder( + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), + builder: (context, child) { + return Transform.scale( + scale: _zoomAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Transform.translate( + offset: _panAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Stack( + fit: StackFit.expand, + children: [ + // Current image + if (_currentAsset != null) + Opacity( + opacity: _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_currentAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return Container(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + + if (_nextAsset != null) + Opacity( + opacity: 1.0 - _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_nextAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return const SizedBox.shrink(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 3d56f3f657..af5b9a7ce7 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -267,7 +267,7 @@ class _PhotoViewGalleryState extends State { key: pageOption.key ?? ObjectKey(index), childSize: pageOption.childSize, backgroundDecoration: widget.backgroundDecoration, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, @@ -303,7 +303,7 @@ class _PhotoViewGalleryState extends State { loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, semanticLabel: pageOption.semanticLabel, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, onPageBuild: widget.onPageBuild, controllerCallbackBuilder: _getControllerCallbackBuilder, diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 42f37fbc85..aa78658006 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1556,6 +1556,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + scroll_date_picker: + dependency: "direct main" + description: + name: scroll_date_picker + sha256: "1b00a3e24d92c77aa84d5856cfe6a57fd5df5f645ce1a6af0feb3ec84bdffb34" + url: "https://pub.dev" + source: hosted + version: "3.8.0" scrollable_positioned_list: dependency: "direct main" description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 03519a0cf2..8510537a1f 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -72,6 +72,7 @@ dependencies: uuid: ^4.5.1 wakelock_plus: ^1.2.10 worker_manager: ^7.2.3 + scroll_date_picker: ^3.8.0 native_video_player: git: From 444133a72be21fcd837e79da0d4de2e7226a4a1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 10:36:50 +0000 Subject: [PATCH 137/169] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.3.0 docker digest to 0e763a2 (#20380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index ccb21e4587..b923eb0579 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -38,7 +38,7 @@ services: image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:0e763a2383d56f90364fcd72767ac41400cd30d2627f407f7e7960c9f1923c21 command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres From d8a65528119ca839b2aef6f4883b0325fcb1a30a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:29:37 -0400 Subject: [PATCH 138/169] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 docker digest to 32324a2 (#20381) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 0d918a3dbe..d9a321240a 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -123,7 +123,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 838aea4458..6ed0aeee8e 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -63,7 +63,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2c0895a57a..ed4e921ff6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -56,7 +56,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} From 4ea4ee40afe0a1a72773c2014099db7be9c5a93f Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Wed, 30 Jul 2025 08:31:16 -0400 Subject: [PATCH 139/169] fix(web): Search chip key value heights don't match (#20312) - add flex items-stretch to stretch chip key height to match value height --- .../search/[[photos=photos]]/[[assetId=id]]/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index acb7176a7c..48ead6e6b4 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -330,9 +330,9 @@ > {#each getObjectKeys(terms) as searchKey (searchKey)} {@const value = terms[searchKey]} -
+
{getHumanReadableSearchKey(searchKey as keyof SearchTerms)} From baadf9db20ec7be633283c6a60b144d0ba8b7cb7 Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:39:19 -0400 Subject: [PATCH 140/169] fix(web): timeline date group width (#19964) Fix the calculation for the date group width, so there's never a scenario where photos will be hidden. On mobile devices, photos in the second row can sometimes have a top of <100px, which throws off the calculation of the date group width. --- web/src/lib/utils/layout-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/utils/layout-utils.ts b/web/src/lib/utils/layout-utils.ts index 429055b92a..e850371b16 100644 --- a/web/src/lib/utils/layout-utils.ts +++ b/web/src/lib/utils/layout-utils.ts @@ -57,7 +57,7 @@ class Adapter { this.result = result; this.width = 0; for (const box of this.result.boxes) { - if (box.top < 100) { + if (box.top === 0) { this.width = box.left + box.width; } else { break; From 9f20522df58bb68c3059d2bd9d13aef02b309b36 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 19:14:19 +0530 Subject: [PATCH 141/169] chore: add isFavorite to PlatformAsset in duplicate check (#20427) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/ios/Runner/Sync/MessagesImpl.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index b8d97b0a82..2810dee7c1 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -172,7 +172,8 @@ class NativeSyncApiImpl: NativeSyncApi { name: "", type: 0, durationInSeconds: 0, - orientation: 0 + orientation: 0, + isFavorite: false ) if (updatedAssets.contains(AssetWrapper(with: predicate))) { continue From da5deffd03797b28a15380251217ea720dbe528b Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:46:23 +0530 Subject: [PATCH 142/169] fix: exclude assets from excluded albumbs on main timeline (#20425) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../entities/merged_asset.drift | 28 ++++++++++++------- .../entities/merged_asset.drift.dart | 6 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 579323d641..d1377f6685 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -64,6 +64,11 @@ AND EXISTS ( INNER JOIN local_album_entity la on laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) +AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded +) ORDER BY created_at DESC LIMIT $limit; @@ -95,16 +100,19 @@ FROM lae.created_at FROM local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - LEFT JOIN - local_album_asset_entity laa ON laa.asset_id = lae.id - LEFT JOIN - local_album_entity la ON la.id = laa.album_id - WHERE - rae.id IS NULL - AND rae.owner_id IN :user_ids - AND la.backup_selection = 0 -- selected + WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids + ) + AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected + ) + AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded + ) ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 9916ec13bb..5a091c349c 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor { ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ for (var $ in userIds) i0.Variable($), ...generatedlimit.introducedVariables, @@ -68,13 +68,13 @@ class MergedAssetDrift extends i1.ModularAccessor { i0.Selectable mergedBucket({ required int groupBy, - required List userIds, + required List userIds, }) { var $arrayStartIndex = 2; final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND rae.owner_id IN ($expandeduserIds) AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2)) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), for (var $ in userIds) i0.Variable($), From 097e132fba660b9fdab50a14fb22002aafe5d551 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 30 Jul 2025 11:09:28 -0500 Subject: [PATCH 143/169] fix: user profile images not working in beta timeline (#20203) * fix user icons in album view * revert updateUsersV1 change * fix: UserDto merge issues * fix: update user entity * revert what I thought were merge issues turns out drift cant figure out when it needs to gen a file... * fix removed line * handle defaults for older servers * feat: checkpoint migrations * fix: use parenthesis instead of brackets * Update 1753800911775-ProfileImageCheckpointRemoval.ts * fix: sync stream updateUsersV1 --- .../drift_schemas/main/drift_schema_v5.json | 1 + mobile/lib/domain/models/user.model.dart | 32 +- mobile/lib/domain/services/user.service.dart | 2 +- .../infrastructure/entities/user.entity.dart | 15 +- .../entities/user.entity.drift.dart | 333 +- .../repositories/db.repository.dart | 11 +- .../repositories/db.repository.steps.dart | 373 + .../repositories/remote_album.repository.dart | 5 +- .../repositories/sync_stream.repository.dart | 7 +- .../infrastructure/utils/user.converter.dart | 13 +- .../pages/drift_user_selection.page.dart | 5 +- mobile/lib/providers/auth.provider.dart | 1 - mobile/lib/utils/openapi_patching.dart | 5 + .../widgets/common/user_circle_avatar.dart | 11 +- mobile/openapi/lib/model/sync_user_v1.dart | 22 +- .../domain/services/user_service_test.dart | 4 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v5.dart | 6402 +++++++++++++++++ mobile/test/fixtures/sync_stream.stub.dart | 20 +- mobile/test/fixtures/user.stub.dart | 6 +- .../modules/shared/sync_service_test.dart | 9 +- open-api/immich-openapi-specs.json | 11 +- server/src/database.ts | 2 +- server/src/dtos/sync.dto.ts | 4 +- server/src/queries/sync.repository.sql | 10 +- server/src/repositories/sync.repository.ts | 11 +- ...800911775-ProfileImageCheckpointRemoval.ts | 25 + server/src/services/sync.service.ts | 4 +- .../test/medium/specs/sync/sync-user.spec.ts | 2 + 29 files changed, 7069 insertions(+), 282 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v5.json create mode 100644 mobile/test/drift/main/generated/schema_v5.dart create mode 100644 server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts diff --git a/mobile/drift_schemas/main/drift_schema_v5.json b/mobile/drift_schemas/main/drift_schema_v5.json new file mode 100644 index 0000000000..ce29eaabdc --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v5.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 1e83fa498d..b0a66f7d70 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -11,7 +11,6 @@ class UserDto { final bool isAdmin; final DateTime updatedAt; - final String? profileImagePath; final AvatarColor avatarColor; final bool memoryEnabled; @@ -25,18 +24,22 @@ class UserDto { bool get hasQuota => quotaSizeInBytes > 0; + final bool hasProfileImage; + final DateTime profileChangedAt; + const UserDto({ required this.id, required this.email, required this.name, required this.isAdmin, required this.updatedAt, - this.profileImagePath, + required this.profileChangedAt, this.avatarColor = AvatarColor.primary, this.memoryEnabled = true, this.inTimeline = false, this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, + this.hasProfileImage = false, this.quotaUsageInBytes = 0, this.quotaSizeInBytes = 0, }); @@ -49,14 +52,13 @@ email: $email, name: $name, isAdmin: $isAdmin, updatedAt: $updatedAt, -profileImagePath: ${profileImagePath ?? ''}, avatarColor: $avatarColor, memoryEnabled: $memoryEnabled, inTimeline: $inTimeline, isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedWith: $isPartnerSharedWith, -quotaUsageInBytes: $quotaUsageInBytes, -quotaSizeInBytes: $quotaSizeInBytes, +hasProfileImage: $hasProfileImage +profileChangedAt: $profileChangedAt }'''; } @@ -66,28 +68,26 @@ quotaSizeInBytes: $quotaSizeInBytes, String? name, bool? isAdmin, DateTime? updatedAt, - String? profileImagePath, AvatarColor? avatarColor, bool? memoryEnabled, bool? inTimeline, bool? isPartnerSharedBy, bool? isPartnerSharedWith, - int? quotaUsageInBytes, - int? quotaSizeInBytes, + bool? hasProfileImage, + DateTime? profileChangedAt, }) => UserDto( id: id ?? this.id, email: email ?? this.email, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, avatarColor: avatarColor ?? this.avatarColor, memoryEnabled: memoryEnabled ?? this.memoryEnabled, inTimeline: inTimeline ?? this.inTimeline, isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, ); @override @@ -101,12 +101,11 @@ quotaSizeInBytes: $quotaSizeInBytes, other.name == name && other.isPartnerSharedBy == isPartnerSharedBy && other.isPartnerSharedWith == isPartnerSharedWith && - other.profileImagePath == profileImagePath && other.isAdmin == isAdmin && other.memoryEnabled == memoryEnabled && other.inTimeline == inTimeline && - other.quotaUsageInBytes == quotaUsageInBytes && - other.quotaSizeInBytes == quotaSizeInBytes; + other.hasProfileImage == hasProfileImage && + other.profileChangedAt.isAtSameMomentAs(profileChangedAt); } @override @@ -116,14 +115,13 @@ quotaSizeInBytes: $quotaSizeInBytes, email.hashCode ^ updatedAt.hashCode ^ isAdmin.hashCode ^ - profileImagePath.hashCode ^ avatarColor.hashCode ^ memoryEnabled.hashCode ^ inTimeline.hashCode ^ isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; + hasProfileImage.hashCode ^ + profileChangedAt.hashCode; } class PartnerUserDto { diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 3e948fe0f5..d347d8aa4f 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -45,7 +45,7 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { final path = await _userApiRepository.createProfileImage(name: name, data: image); - final updatedUser = getMyUser().copyWith(profileImagePath: path); + final updatedUser = getMyUser(); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); return path; diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index ab5b9a5621..78fc76b45d 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -50,12 +50,10 @@ class User { isAdmin: dto.isAdmin, isPartnerSharedBy: dto.isPartnerSharedBy, isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", + profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "", avatarColor: dto.avatarColor, memoryEnabled: dto.memoryEnabled, inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, ); UserDto toDto() => UserDto( @@ -64,12 +62,13 @@ class User { name: name, isAdmin: isAdmin, updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, avatarColor: avatarColor, memoryEnabled: memoryEnabled, inTimeline: inTimeline, isPartnerSharedBy: isPartnerSharedBy, isPartnerSharedWith: isPartnerSharedWith, + hasProfileImage: profileImagePath.isNotEmpty, + profileChangedAt: updatedAt, quotaUsageInBytes: quotaUsageInBytes, quotaSizeInBytes: quotaSizeInBytes, ); @@ -82,11 +81,11 @@ class UserEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); - TextColumn get profileImagePath => text().nullable()(); + + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - // Quota - IntColumn get quotaSizeInBytes => integer().nullable()(); - IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 2c3c8a1f9c..dbfddab4a0 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -12,10 +12,9 @@ typedef $$UserEntityTableCreateCompanionBuilder = required String name, i0.Value isAdmin, required String email, - i0.Value profileImagePath, + i0.Value hasProfileImage, + i0.Value profileChangedAt, i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, }); typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion Function({ @@ -23,10 +22,9 @@ typedef $$UserEntityTableUpdateCompanionBuilder = i0.Value name, i0.Value isAdmin, i0.Value email, - i0.Value profileImagePath, + i0.Value hasProfileImage, + i0.Value profileChangedAt, i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, }); class $$UserEntityTableFilterComposer @@ -58,8 +56,13 @@ class $$UserEntityTableFilterComposer builder: (column) => i0.ColumnFilters(column), ); - i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.ColumnFilters get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => i0.ColumnFilters(column), ); @@ -67,16 +70,6 @@ class $$UserEntityTableFilterComposer column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column), ); - - i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column), - ); - - i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column), - ); } class $$UserEntityTableOrderingComposer @@ -108,8 +101,13 @@ class $$UserEntityTableOrderingComposer builder: (column) => i0.ColumnOrderings(column), ); - i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.ColumnOrderings get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => i0.ColumnOrderings(column), ); @@ -117,16 +115,6 @@ class $$UserEntityTableOrderingComposer column: $table.updatedAt, builder: (column) => i0.ColumnOrderings(column), ); - - i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column), - ); - - i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column), - ); } class $$UserEntityTableAnnotationComposer @@ -150,23 +138,18 @@ class $$UserEntityTableAnnotationComposer i0.GeneratedColumn get email => $composableBuilder(column: $table.email, builder: (column) => column); - i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.GeneratedColumn get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => column, + ); + + i0.GeneratedColumn get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => column, ); i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => column, - ); - - i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => column, - ); } class $$UserEntityTableTableManager @@ -210,19 +193,17 @@ class $$UserEntityTableTableManager i0.Value name = const i0.Value.absent(), i0.Value isAdmin = const i0.Value.absent(), i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), }) => i1.UserEntityCompanion( id: id, name: name, isAdmin: isAdmin, email: email, - profileImagePath: profileImagePath, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, ), createCompanionCallback: ({ @@ -230,19 +211,17 @@ class $$UserEntityTableTableManager required String name, i0.Value isAdmin = const i0.Value.absent(), required String email, - i0.Value profileImagePath = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), }) => i1.UserEntityCompanion.insert( id: id, name: name, isAdmin: isAdmin, email: email, - profileImagePath: profileImagePath, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) @@ -326,16 +305,32 @@ class $UserEntityTable extends i2.UserEntity type: i0.DriftSqlType.string, requiredDuringInsert: true, ); - static const i0.VerificationMeta _profileImagePathMeta = - const i0.VerificationMeta('profileImagePath'); + static const i0.VerificationMeta _hasProfileImageMeta = + const i0.VerificationMeta('hasProfileImage'); @override - late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn( - 'profile_image_path', + late final i0.GeneratedColumn hasProfileImage = + i0.GeneratedColumn( + 'has_profile_image', aliasedName, - true, - type: i0.DriftSqlType.string, + false, + type: i0.DriftSqlType.bool, requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _profileChangedAtMeta = + const i0.VerificationMeta('profileChangedAt'); + @override + late final i0.GeneratedColumn profileChangedAt = + i0.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, ); static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( 'updatedAt', @@ -350,38 +345,15 @@ class $UserEntityTable extends i2.UserEntity requiredDuringInsert: false, defaultValue: i3.currentDateAndTime, ); - static const i0.VerificationMeta _quotaSizeInBytesMeta = - const i0.VerificationMeta('quotaSizeInBytes'); - @override - late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', - aliasedName, - true, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - ); - static const i0.VerificationMeta _quotaUsageInBytesMeta = - const i0.VerificationMeta('quotaUsageInBytes'); - @override - late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn( - 'quota_usage_in_bytes', - aliasedName, - false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0), - ); @override List get $columns => [ id, name, isAdmin, email, - profileImagePath, + hasProfileImage, + profileChangedAt, updatedAt, - quotaSizeInBytes, - quotaUsageInBytes, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -422,12 +394,21 @@ class $UserEntityTable extends i2.UserEntity } else if (isInserting) { context.missing(_emailMeta); } - if (data.containsKey('profile_image_path')) { + if (data.containsKey('has_profile_image')) { context.handle( - _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, - _profileImagePathMeta, + _hasProfileImageMeta, + hasProfileImage.isAcceptableOrUnknown( + data['has_profile_image']!, + _hasProfileImageMeta, + ), + ); + } + if (data.containsKey('profile_changed_at')) { + context.handle( + _profileChangedAtMeta, + profileChangedAt.isAcceptableOrUnknown( + data['profile_changed_at']!, + _profileChangedAtMeta, ), ); } @@ -437,24 +418,6 @@ class $UserEntityTable extends i2.UserEntity updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), ); } - if (data.containsKey('quota_size_in_bytes')) { - context.handle( - _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, - _quotaSizeInBytesMeta, - ), - ); - } - if (data.containsKey('quota_usage_in_bytes')) { - context.handle( - _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, - _quotaUsageInBytesMeta, - ), - ); - } return context; } @@ -480,22 +443,18 @@ class $UserEntityTable extends i2.UserEntity i0.DriftSqlType.string, data['${effectivePrefix}email'], )!, - profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}profile_image_path'], - ), + hasProfileImage: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'], )!, - quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, - data['${effectivePrefix}quota_size_in_bytes'], - ), - quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, - data['${effectivePrefix}quota_usage_in_bytes'], - )!, ); } @@ -516,19 +475,17 @@ class UserEntityData extends i0.DataClass final String name; final bool isAdmin; final String email; - final String? profileImagePath; + final bool hasProfileImage; + final DateTime profileChangedAt; final DateTime updatedAt; - final int? quotaSizeInBytes; - final int quotaUsageInBytes; const UserEntityData({ required this.id, required this.name, required this.isAdmin, required this.email, - this.profileImagePath, + required this.hasProfileImage, + required this.profileChangedAt, required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes, }); @override Map toColumns(bool nullToAbsent) { @@ -537,14 +494,9 @@ class UserEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['is_admin'] = i0.Variable(isAdmin); map['email'] = i0.Variable(email); - if (!nullToAbsent || profileImagePath != null) { - map['profile_image_path'] = i0.Variable(profileImagePath); - } + map['has_profile_image'] = i0.Variable(hasProfileImage); + map['profile_changed_at'] = i0.Variable(profileChangedAt); map['updated_at'] = i0.Variable(updatedAt); - if (!nullToAbsent || quotaSizeInBytes != null) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); - } - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); return map; } @@ -558,10 +510,9 @@ class UserEntityData extends i0.DataClass name: serializer.fromJson(json['name']), isAdmin: serializer.fromJson(json['isAdmin']), email: serializer.fromJson(json['email']), - profileImagePath: serializer.fromJson(json['profileImagePath']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), updatedAt: serializer.fromJson(json['updatedAt']), - quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), - quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), ); } @override @@ -572,10 +523,9 @@ class UserEntityData extends i0.DataClass 'name': serializer.toJson(name), 'isAdmin': serializer.toJson(isAdmin), 'email': serializer.toJson(email), - 'profileImagePath': serializer.toJson(profileImagePath), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), 'updatedAt': serializer.toJson(updatedAt), - 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), - 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), }; } @@ -584,23 +534,17 @@ class UserEntityData extends i0.DataClass String? name, bool? isAdmin, String? email, - i0.Value profileImagePath = const i0.Value.absent(), + bool? hasProfileImage, + DateTime? profileChangedAt, DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes, }) => i1.UserEntityData( id: id ?? this.id, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( @@ -608,16 +552,13 @@ class UserEntityData extends i0.DataClass name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present - ? data.profileImagePath.value - : this.profileImagePath, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present - ? data.quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present - ? data.quotaUsageInBytes.value - : this.quotaUsageInBytes, ); } @@ -628,10 +569,9 @@ class UserEntityData extends i0.DataClass ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } @@ -642,10 +582,9 @@ class UserEntityData extends i0.DataClass name, isAdmin, email, - profileImagePath, + hasProfileImage, + profileChangedAt, updatedAt, - quotaSizeInBytes, - quotaUsageInBytes, ); @override bool operator ==(Object other) => @@ -655,10 +594,9 @@ class UserEntityData extends i0.DataClass other.name == this.name && other.isAdmin == this.isAdmin && other.email == this.email && - other.profileImagePath == this.profileImagePath && - other.updatedAt == this.updatedAt && - other.quotaSizeInBytes == this.quotaSizeInBytes && - other.quotaUsageInBytes == this.quotaUsageInBytes); + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); } class UserEntityCompanion extends i0.UpdateCompanion { @@ -666,29 +604,26 @@ class UserEntityCompanion extends i0.UpdateCompanion { final i0.Value name; final i0.Value isAdmin; final i0.Value email; - final i0.Value profileImagePath; + final i0.Value hasProfileImage; + final i0.Value profileChangedAt; final i0.Value updatedAt; - final i0.Value quotaSizeInBytes; - final i0.Value quotaUsageInBytes; const UserEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), this.isAdmin = const i0.Value.absent(), this.email = const i0.Value.absent(), - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), }); UserEntityCompanion.insert({ required String id, required String name, this.isAdmin = const i0.Value.absent(), required String email, - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), }) : id = i0.Value(id), name = i0.Value(name), email = i0.Value(email); @@ -697,20 +632,18 @@ class UserEntityCompanion extends i0.UpdateCompanion { i0.Expression? name, i0.Expression? isAdmin, i0.Expression? email, - i0.Expression? profileImagePath, + i0.Expression? hasProfileImage, + i0.Expression? profileChangedAt, i0.Expression? updatedAt, - i0.Expression? quotaSizeInBytes, - i0.Expression? quotaUsageInBytes, }) { return i0.RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, if (isAdmin != null) 'is_admin': isAdmin, if (email != null) 'email': email, - if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, if (updatedAt != null) 'updated_at': updatedAt, - if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, - if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, }); } @@ -719,20 +652,18 @@ class UserEntityCompanion extends i0.UpdateCompanion { i0.Value? name, i0.Value? isAdmin, i0.Value? email, - i0.Value? profileImagePath, + i0.Value? hasProfileImage, + i0.Value? profileChangedAt, i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes, }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath ?? this.profileImagePath, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, ); } @@ -751,18 +682,15 @@ class UserEntityCompanion extends i0.UpdateCompanion { if (email.present) { map['email'] = i0.Variable(email.value); } - if (profileImagePath.present) { - map['profile_image_path'] = i0.Variable(profileImagePath.value); + if (hasProfileImage.present) { + map['has_profile_image'] = i0.Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = i0.Variable(profileChangedAt.value); } if (updatedAt.present) { map['updated_at'] = i0.Variable(updatedAt.value); } - if (quotaSizeInBytes.present) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); - } - if (quotaUsageInBytes.present) { - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); - } return map; } @@ -773,10 +701,9 @@ class UserEntityCompanion extends i0.UpdateCompanion { ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 6e574afa8c..353cabf31e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 4; + int get schemaVersion => 5; @override MigrationStrategy get migration => MigrationStrategy( @@ -94,6 +94,15 @@ class Drift extends $Drift implements IDatabaseRepository { // asset_face_entity is added await m.create(v4.assetFaceEntity); }, + from4To5: (m, v5) async { + await m.alterTable( + TableMigration( + v5.userEntity, + newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt], + columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime}, + ), + ); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 5bf20780f4..8129bba00c 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1954,10 +1954,376 @@ i1.GeneratedColumn _column_83(String aliasedName) => false, type: i1.DriftSqlType.string, ); + +final class Schema5 extends i0.VersionedSchema { + Schema5({required super.database}) : super(version: 5); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape16 extends i0.VersionedTable { + Shape16({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_84(String aliasedName) => + i1.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_85(String aliasedName) => + i1.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -1976,6 +2342,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from3To4(migrator, schema); return 4; + case 4: + final schema = Schema5(database: database); + final migrator = i1.Migrator(database, schema); + await from4To5(migrator, schema); + return 5; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -1986,10 +2357,12 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, from3To4: from3To4, + from4To5: from4To5, ), ); diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index 79f3d78fd7..6bc6a7066d 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -173,15 +173,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { id: user.id, email: user.email, name: user.name, - profileImagePath: user.profileImagePath?.isEmpty == true ? null : user.profileImagePath, isAdmin: user.isAdmin, updatedAt: user.updatedAt, - quotaSizeInBytes: user.quotaSizeInBytes ?? 0, - quotaUsageInBytes: user.quotaUsageInBytes, memoryEnabled: true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, + profileChangedAt: user.profileChangedAt, + hasProfileImage: user.hasProfileImage, ), ) .get(); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 64b0661e16..2eefa298f8 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -42,7 +42,12 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final user in data) { - final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email)); + final companion = UserEntityCompanion( + name: Value(user.name), + email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), + ); batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index 19958beabc..dc107e6fb2 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -11,7 +11,8 @@ abstract final class UserConverter { name: dto.name, isAdmin: false, updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, + hasProfileImage: dto.profileImagePath.isNotEmpty, + profileChangedAt: dto.profileChangedAt, avatarColor: dto.avatarColor.toAvatarColor(), ); @@ -21,14 +22,13 @@ abstract final class UserConverter { name: adminDto.name, isAdmin: adminDto.isAdmin, updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, avatarColor: adminDto.avatarColor.toAvatarColor(), memoryEnabled: preferenceDto?.memories.enabled ?? true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + profileChangedAt: adminDto.profileChangedAt, + hasProfileImage: adminDto.profileImagePath.isNotEmpty, ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( @@ -37,14 +37,13 @@ abstract final class UserConverter { name: dto.name, isAdmin: false, updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, avatarColor: dto.avatarColor.toAvatarColor(), memoryEnabled: false, inTimeline: dto.inTimeline ?? false, isPartnerSharedBy: false, isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, + profileChangedAt: dto.profileChangedAt, + hasProfileImage: dto.profileImagePath.isNotEmpty, ); } diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index e8835e7146..5bd32aaf81 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -27,15 +27,14 @@ final driftUsersProvider = FutureProvider.autoDispose>((ref) async name: entity.name, email: entity.email, isAdmin: entity.isAdmin, - profileImagePath: entity.profileImagePath, updatedAt: entity.updatedAt, - quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, - quotaUsageInBytes: entity.quotaUsageInBytes, isPartnerSharedBy: false, isPartnerSharedWith: false, avatarColor: AvatarColor.primary, memoryEnabled: true, inTimeline: true, + profileChangedAt: entity.profileChangedAt, + hasProfileImage: entity.hasProfileImage, ), ) .toList(); diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index fc3e08472b..02f7920d6f 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -167,7 +167,6 @@ class AuthNotifier extends StateNotifier { isAuthenticated: true, name: user.name, isAdmin: user.isAdmin, - profileImagePath: user.profileImagePath, ); return true; diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index efbcf1c139..3e45390198 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -40,6 +40,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'isOnboarded', false); } break; + case 'SyncUserV1': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + addDefault(value, 'hasProfileImage', false); + } } } diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 8be71e9b2e..1fbfce6c53 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -32,6 +32,7 @@ class UserCircleAvatar extends ConsumerWidget { ), child: Text(user.name[0].toUpperCase()), ); + return Tooltip( message: user.name, child: Container( @@ -42,13 +43,12 @@ class UserCircleAvatar extends ConsumerWidget { child: CircleAvatar( backgroundColor: userAvatarColor, radius: radius, - child: user.profileImagePath == null - ? textIcon - : ClipRRect( + child: user.hasProfileImage + ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), child: CachedNetworkImage( fit: BoxFit.cover, - cacheKey: user.profileImagePath, + cacheKey: user.profileChangedAt.toIso8601String(), width: size, height: size, placeholder: (_, __) => Image.memory(kTransparentImage), @@ -57,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget { fadeInDuration: const Duration(milliseconds: 300), errorWidget: (context, error, stackTrace) => textIcon, ), - ), + ) + : textIcon, ), ), ); diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index c01ddcc9fc..b9fad5ae8c 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -16,8 +16,10 @@ class SyncUserV1 { required this.avatarColor, required this.deletedAt, required this.email, + required this.hasProfileImage, required this.id, required this.name, + required this.profileChangedAt, }); UserAvatarColor? avatarColor; @@ -26,17 +28,23 @@ class SyncUserV1 { String email; + bool hasProfileImage; + String id; String name; + DateTime profileChangedAt; + @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && + other.hasProfileImage == hasProfileImage && other.id == id && - other.name == name; + other.name == name && + other.profileChangedAt == profileChangedAt; @override int get hashCode => @@ -44,11 +52,13 @@ class SyncUserV1 { (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + + (hasProfileImage.hashCode) + (id.hashCode) + - (name.hashCode); + (name.hashCode) + + (profileChangedAt.hashCode); @override - String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, name=$name, profileChangedAt=$profileChangedAt]'; Map toJson() { final json = {}; @@ -63,8 +73,10 @@ class SyncUserV1 { // json[r'deletedAt'] = null; } json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; json[r'id'] = this.id; json[r'name'] = this.name; + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); return json; } @@ -80,8 +92,10 @@ class SyncUserV1 { avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, ); } return null; @@ -132,8 +146,10 @@ class SyncUserV1 { 'avatarColor', 'deletedAt', 'email', + 'hasProfileImage', 'id', 'name', + 'profileChangedAt', }; } diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index b3d967154c..395f38a207 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -98,7 +98,7 @@ void main() { group('createProfileImage', () { test('should return profile image path', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), @@ -115,7 +115,7 @@ void main() { test('should return null if profile image creation fails', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 22131b11bb..c42542afb3 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -7,6 +7,7 @@ import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; +import 'schema_v5.dart' as v5; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -20,10 +21,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v3.DatabaseAtV3(db); case 4: return v4.DatabaseAtV4(db); + case 5: + return v5.DatabaseAtV5(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4]; + static const versions = const [1, 2, 3, 4, 5]; } diff --git a/mobile/test/drift/main/generated/schema_v5.dart b/mobile/test/drift/main/generated/schema_v5.dart new file mode 100644 index 0000000000..5c94ff26cb --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v5.dart @@ -0,0 +1,6402 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV5 extends GeneratedDatabase { + DatabaseAtV5(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 5; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index a89be5ad24..23c750d6d9 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -4,12 +4,28 @@ import 'package:openapi/api.dart'; abstract final class SyncStreamStub { static final userV1Admin = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1(deletedAt: DateTime(2020), email: "admin@admin", id: "1", name: "Admin", avatarColor: null), + data: SyncUserV1( + deletedAt: DateTime(2020), + email: "admin@admin", + id: "1", + name: "Admin", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), + ), ack: "1", ); static final userV1User = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1(deletedAt: DateTime(2021), email: "user@user", id: "5", name: "User", avatarColor: null), + data: SyncUserV1( + deletedAt: DateTime(2021), + email: "user@user", + id: "5", + name: "User", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), + ), ack: "5", ); static final userDeleteV1 = SyncEvent( diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 764342520f..369e62440d 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -10,7 +10,7 @@ abstract final class UserStub { name: "admin", isAdmin: true, updatedAt: DateTime(2021), - profileImagePath: null, + profileChangedAt: DateTime(2021), avatarColor: AvatarColor.green, ); @@ -20,7 +20,7 @@ abstract final class UserStub { name: "user1", isAdmin: false, updatedAt: DateTime(2022), - profileImagePath: null, + profileChangedAt: DateTime(2022), avatarColor: AvatarColor.red, ); @@ -30,7 +30,7 @@ abstract final class UserStub { name: "user2", isAdmin: false, updatedAt: DateTime(2023), - profileImagePath: null, + profileChangedAt: DateTime(2023), avatarColor: AvatarColor.primary, ); } diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index b51a4d67fd..22fd3cacfc 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -66,7 +66,14 @@ void main() { final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); - final owner = UserDto(id: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", isAdmin: false); + final owner = UserDto( + id: "1", + updatedAt: DateTime.now(), + email: "a@b.c", + name: "first last", + isAdmin: false, + profileChangedAt: DateTime(2021), + ); late SyncService s; setUpAll(() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 1e9fddf79d..8c491ca471 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -15124,19 +15124,28 @@ "email": { "type": "string" }, + "hasProfileImage": { + "type": "boolean" + }, "id": { "type": "string" }, "name": { "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" } }, "required": [ "avatarColor", "deletedAt", "email", + "hasProfileImage", "id", - "name" + "name", + "profileChangedAt" ], "type": "object" }, diff --git a/server/src/database.ts b/server/src/database.ts index 44bdefa080..052955636b 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -357,7 +357,7 @@ export const columns = { ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], - syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId', 'profileImagePath', 'profileChangedAt'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 92aea8f5e9..66061e7bbe 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -62,6 +62,8 @@ export class SyncUserV1 { @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; + hasProfileImage!: boolean; + profileChangedAt!: Date; } @ExtraModel() @@ -74,8 +76,6 @@ export class SyncAuthUserV1 extends SyncUserV1 { quotaSizeInBytes!: number | null; @ApiProperty({ type: 'integer' }) quotaUsageInBytes!: number; - hasProfileImage!: boolean; - profileChangedAt!: Date; } @ExtraModel() diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7c7774a020..5c80460158 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -452,14 +452,14 @@ select "avatarColor", "deletedAt", "updateId", + "profileImagePath", + "profileChangedAt", "isAdmin", "pinCode", "oauthId", "storageLabel", "quotaSizeInBytes", - "quotaUsageInBytes", - "profileImagePath", - "profileChangedAt" + "quotaUsageInBytes" from "user" where @@ -896,7 +896,9 @@ select "email", "avatarColor", "deletedAt", - "updateId" + "updateId", + "profileImagePath", + "profileChangedAt" from "user" where diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 486984118d..d72ddcfc4d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -375,16 +375,7 @@ class AuthUserSync extends BaseSync { return this.db .selectFrom('user') .select(columns.syncUser) - .select([ - 'isAdmin', - 'pinCode', - 'oauthId', - 'storageLabel', - 'quotaSizeInBytes', - 'quotaUsageInBytes', - 'profileImagePath', - 'profileChangedAt', - ]) + .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) .$call(this.upsertTableFilters(ack)) .stream(); } diff --git a/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts new file mode 100644 index 0000000000..4f741f2113 --- /dev/null +++ b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts @@ -0,0 +1,25 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 57b953f12e..fee77f35ba 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -188,8 +188,8 @@ export class SyncService extends BaseService { const upsertType = SyncEntityType.UserV1; const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 72661e119c..c5d572d7d6 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -35,9 +35,11 @@ describe(SyncEntityType.UserV1, () => { data: { deletedAt: user.deletedAt, email: user.email, + hasProfileImage: user.profileImagePath !== '', id: user.id, name: user.name, avatarColor: user.avatarColor, + profileChangedAt: user.profileChangedAt.toISOString(), }, type: 'UserV1', }, From d5a01c03105c0f59398134385210167d5ed4cf92 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 12:21:02 -0400 Subject: [PATCH 144/169] fix(web): timeline time bucket issue (#20438) --- .../components/photos-page/asset-grid.svelte | 9 ++---- .../group-insertion-cache.svelte.ts | 6 ++-- .../internal/load-support.svelte.ts | 1 - .../internal/operations-support.svelte.ts | 4 +-- .../internal/search-support.svelte.ts | 6 ++-- .../timeline-manager/month-group.svelte.ts | 10 +++---- .../timeline-manager.svelte.ts | 8 ++--- .../lib/managers/timeline-manager/types.ts | 8 ++--- web/src/lib/utils/timeline-util.ts | 29 +++++++++---------- 9 files changed, 36 insertions(+), 45 deletions(-) diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index bd7900f2b6..808a4cf54c 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -29,12 +29,7 @@ import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions'; import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils'; import { navigate } from '$lib/utils/navigation'; - import { - getTimes, - toTimelineAsset, - type ScrubberListener, - type TimelinePlainYearMonth, - } from '$lib/utils/timeline-util'; + import { getTimes, toTimelineAsset, type ScrubberListener, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk'; import { modalManager } from '@immich/ui'; import { DateTime } from 'luxon'; @@ -343,7 +338,7 @@ const monthsLength = timelineManager.months.length; for (let i = -1; i < monthsLength + 1; i++) { - let monthGroup: TimelinePlainYearMonth | undefined; + let monthGroup: TimelineYearMonth | undefined; let monthGroupHeight = 0; if (i === -1) { // lead-in diff --git a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts index e511df9bf0..aa4bae8919 100644 --- a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts +++ b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts @@ -1,4 +1,4 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import { SvelteSet } from 'svelte/reactivity'; import type { DayGroup } from './day-group.svelte'; @@ -13,11 +13,11 @@ export class GroupInsertionCache { changedDayGroups = new SvelteSet(); newDayGroups = new SvelteSet(); - getDayGroup({ year, month, day }: TimelinePlainDate): DayGroup | undefined { + getDayGroup({ year, month, day }: TimelineDate): DayGroup | undefined { return this.#lookupCache[year]?.[month]?.[day]; } - setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelinePlainDate) { + setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelineDate) { if (!this.#lookupCache[year]) { this.#lookupCache[year] = {}; } diff --git a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts index 6146fdb600..82a9e8083d 100644 --- a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts @@ -1,7 +1,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { toISOYearMonthUTC } from '$lib/utils/timeline-util'; import { getTimeBucket } from '@immich/sdk'; - import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { TimelineManagerOptions } from '../types'; diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts index 4419de2103..4bc99c0315 100644 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts @@ -1,4 +1,4 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import { SvelteSet } from 'svelte/reactivity'; @@ -70,7 +70,7 @@ export function runAssetOperation( const changedMonthGroups = new SvelteSet(); let idsToProcess = new SvelteSet(ids); const idsProcessed = new SvelteSet(); - const combinedMoveAssets: { asset: TimelineAsset; date: TimelinePlainDate }[][] = []; + const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = []; for (const month of timelineManager.months) { if (idsToProcess.size > 0) { const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation); diff --git a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts index f85246933f..7e6ae734dc 100644 --- a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts @@ -1,4 +1,4 @@ -import { plainDateTimeCompare, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; @@ -42,7 +42,7 @@ export function findMonthGroupForAsset(timelineManager: TimelineManager, id: str export function getMonthGroupByDate( timelineManager: TimelineManager, - targetYearMonth: TimelinePlainYearMonth, + targetYearMonth: TimelineYearMonth, ): MonthGroup | undefined { return timelineManager.months.find( (month) => month.yearMonth.year === targetYearMonth.year && month.yearMonth.month === targetYearMonth.month, @@ -135,7 +135,7 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass return range; } -export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelinePlainYearMonth) { +export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelineYearMonth) { for (const month of timelineManager.months) { const { year, month: monthNum } = month.yearMonth; if (monthNum === targetYearMonth.month && year === targetYearMonth.year) { diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index 9f7112963a..03d138f680 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -10,8 +10,8 @@ import { fromTimelinePlainYearMonth, getTimes, setDifference, - type TimelinePlainDateTime, - type TimelinePlainYearMonth, + type TimelineDateTime, + type TimelineYearMonth, } from '$lib/utils/timeline-util'; import { t } from 'svelte-i18n'; @@ -47,11 +47,11 @@ export class MonthGroup { isHeightActual: boolean = $state(false); readonly monthGroupTitle: string; - readonly yearMonth: TimelinePlainYearMonth; + readonly yearMonth: TimelineYearMonth; constructor( store: TimelineManager, - yearMonth: TimelinePlainYearMonth, + yearMonth: TimelineYearMonth, initialCount: number, order: AssetOrder = AssetOrder.Desc, ) { @@ -351,7 +351,7 @@ export class MonthGroup { } } - findClosest(target: TimelinePlainDateTime) { + findClosest(target: TimelineDateTime) { const targetDate = fromTimelinePlainDateTime(target); let closest = undefined; let smallestDiff = Infinity; diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index ff8d1b1347..2e31fa9bc1 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -3,7 +3,7 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { CancellableTask } from '$lib/utils/cancellable-task'; -import { toTimelineAsset, type TimelinePlainDateTime, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { clamp, debounce, isEqual } from 'lodash-es'; import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; @@ -387,7 +387,7 @@ export class TimelineManager { }; } - async loadMonthGroup(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }): Promise { + async loadMonthGroup(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise { let cancelable = true; if (options) { cancelable = options.cancelable; @@ -433,7 +433,7 @@ export class TimelineManager { } } - async #loadMonthGroupAtTime(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }) { + async #loadMonthGroupAtTime(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }) { await this.loadMonthGroup(yearMonth, options); return getMonthGroupByDate(this, yearMonth); } @@ -514,7 +514,7 @@ export class TimelineManager { return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier'); } - async getClosestAssetToDate(dateTime: TimelinePlainDateTime) { + async getClosestAssetToDate(dateTime: TimelineDateTime) { const monthGroup = findMonthGroupForDate(this, dateTime); if (!monthGroup) { return; diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index 8e5523758b..18ee0426f3 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -1,4 +1,4 @@ -import type { TimelinePlainDate, TimelinePlainDateTime } from '$lib/utils/timeline-util'; +import type { TimelineDate, TimelineDateTime } from '$lib/utils/timeline-util'; import type { AssetStackResponseDto, AssetVisibility } from '@immich/sdk'; export type AssetApiGetTimeBucketsRequest = Parameters[0]; @@ -17,8 +17,8 @@ export type TimelineAsset = { ownerId: string; ratio: number; thumbhash: string | null; - localDateTime: TimelinePlainDateTime; - fileCreatedAt: TimelinePlainDateTime; + localDateTime: TimelineDateTime; + fileCreatedAt: TimelineDateTime; visibility: AssetVisibility; isFavorite: boolean; isTrashed: boolean; @@ -35,7 +35,7 @@ export type TimelineAsset = { export type AssetOperation = (asset: TimelineAsset) => { remove: boolean }; -export type MoveAsset = { asset: TimelineAsset; date: TimelinePlainDate }; +export type MoveAsset = { asset: TimelineAsset; date: TimelineDate }; export interface Viewport { width: number; diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index dc237c2223..c160c65922 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -7,16 +7,16 @@ import { SvelteSet } from 'svelte/reactivity'; import { get } from 'svelte/store'; // Move type definitions to the top -export type TimelinePlainYearMonth = { +export type TimelineYearMonth = { year: number; month: number; }; -export type TimelinePlainDate = TimelinePlainYearMonth & { +export type TimelineDate = TimelineYearMonth & { day: number; }; -export type TimelinePlainDateTime = TimelinePlainDate & { +export type TimelineDateTime = TimelineDate & { hour: number; minute: number; second: number; @@ -33,29 +33,26 @@ export type ScrubberListener = ( export const fromISODateTime = (isoDateTime: string, timeZone: string): DateTime => DateTime.fromISO(isoDateTime, { zone: timeZone, locale: get(locale) }) as DateTime; -export const fromISODateTimeToObject = (isoDateTime: string, timeZone: string): TimelinePlainDateTime => +export const fromISODateTimeToObject = (isoDateTime: string, timeZone: string): TimelineDateTime => (fromISODateTime(isoDateTime, timeZone) as DateTime).toObject(); // used for AssetResponseDto.localDateTime, amongst others export const fromISODateTimeUTC = (isoDateTimeUtc: string) => fromISODateTime(isoDateTimeUtc, 'UTC'); -export const fromISODateTimeUTCToObject = (isoDateTimeUtc: string): TimelinePlainDateTime => +export const fromISODateTimeUTCToObject = (isoDateTimeUtc: string): TimelineDateTime => (fromISODateTimeUTC(isoDateTimeUtc) as DateTime).toObject(); // used to create equivalent of AssetResponseDto.localDateTime in UTC, but without timezone information export const fromISODateTimeTruncateTZToObject = ( isoDateTimeUtc: string, timeZone: string | undefined, -): TimelinePlainDateTime => +): TimelineDateTime => ( fromISODateTime(isoDateTimeUtc, timeZone ?? 'UTC').setZone('UTC', { keepLocalTime: true }) as DateTime ).toObject(); // Used to derive a local date time from an ISO string and a UTC offset in hours -export const fromISODateTimeWithOffsetToObject = ( - isoDateTimeUtc: string, - utcOffsetHours: number, -): TimelinePlainDateTime => { +export const fromISODateTimeWithOffsetToObject = (isoDateTimeUtc: string, utcOffsetHours: number): TimelineDateTime => { const utcDateTime = fromISODateTimeUTC(isoDateTimeUtc); // Apply the offset to get the local time @@ -82,23 +79,23 @@ export const getTimes = (isoDateTimeUtc: string, localUtcOffsetHours: number) => }; }; -export const fromTimelinePlainDateTime = (timelineDateTime: TimelinePlainDateTime): DateTime => +export const fromTimelinePlainDateTime = (timelineDateTime: TimelineDateTime): DateTime => DateTime.fromObject(timelineDateTime, { zone: 'local', locale: get(locale) }) as DateTime; -export const fromTimelinePlainDate = (timelineYearMonth: TimelinePlainDate): DateTime => +export const fromTimelinePlainDate = (timelineYearMonth: TimelineDate): DateTime => DateTime.fromObject( { year: timelineYearMonth.year, month: timelineYearMonth.month, day: timelineYearMonth.day }, { zone: 'local', locale: get(locale) }, ) as DateTime; -export const fromTimelinePlainYearMonth = (timelineYearMonth: TimelinePlainYearMonth): DateTime => +export const fromTimelinePlainYearMonth = (timelineYearMonth: TimelineYearMonth): DateTime => DateTime.fromObject( { year: timelineYearMonth.year, month: timelineYearMonth.month }, { zone: 'local', locale: get(locale) }, ) as DateTime; -export const toISOYearMonthUTC = (timelineYearMonth: TimelinePlainYearMonth): string => - (fromTimelinePlainYearMonth(timelineYearMonth).setZone('UTC', { keepLocalTime: true }) as DateTime).toISO(); +export const toISOYearMonthUTC = ({ year, month }: TimelineYearMonth): string => + `${year}-${month.toString().padStart(2, '0')}-01T00:00:00.000Z`; export function formatMonthGroupTitle(_date: DateTime): string { if (!_date.isValid) { @@ -193,7 +190,7 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset => (unknownAsset as TimelineAsset).ratio !== undefined; -export const plainDateTimeCompare = (ascending: boolean, a: TimelinePlainDateTime, b: TimelinePlainDateTime) => { +export const plainDateTimeCompare = (ascending: boolean, a: TimelineDateTime, b: TimelineDateTime) => { const [aDateTime, bDateTime] = ascending ? [a, b] : [b, a]; if (aDateTime.year !== bDateTime.year) { From 749f999f2aade7c3b40ae7fe305d7ec9a2d271bf Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 12:29:36 -0400 Subject: [PATCH 145/169] feat: better endpoint descriptions (#20439) --- mobile/openapi/lib/api/activities_api.dart | 28 +- mobile/openapi/lib/api/albums_api.dart | 75 ++- mobile/openapi/lib/api/api_keys_api.dart | 33 +- mobile/openapi/lib/api/assets_api.dart | 71 ++- .../openapi/lib/api/authentication_api.dart | 28 +- mobile/openapi/lib/api/deprecated_api.dart | 4 +- mobile/openapi/lib/api/download_api.dart | 14 +- mobile/openapi/lib/api/duplicates_api.dart | 19 +- mobile/openapi/lib/api/faces_api.dart | 28 +- mobile/openapi/lib/api/jobs_api.dart | 19 +- mobile/openapi/lib/api/libraries_api.dart | 47 +- mobile/openapi/lib/api/map_api.dart | 34 +- mobile/openapi/lib/api/memories_api.dart | 56 +- mobile/openapi/lib/api/notifications_api.dart | 42 +- mobile/openapi/lib/api/partners_api.dart | 28 +- mobile/openapi/lib/api/people_api.dart | 77 ++- mobile/openapi/lib/api/search_api.dart | 66 +- mobile/openapi/lib/api/server_api.dart | 37 +- mobile/openapi/lib/api/sessions_api.dart | 38 +- mobile/openapi/lib/api/shared_links_api.dart | 69 +- mobile/openapi/lib/api/stacks_api.dart | 49 +- mobile/openapi/lib/api/sync_api.dart | 26 +- mobile/openapi/lib/api/system_config_api.dart | 22 +- .../openapi/lib/api/system_metadata_api.dart | 22 +- mobile/openapi/lib/api/tags_api.dart | 61 +- mobile/openapi/lib/api/timeline_api.dart | 14 +- mobile/openapi/lib/api/trash_api.dart | 17 +- mobile/openapi/lib/api/users_admin_api.dart | 63 +- mobile/openapi/lib/api/users_api.dart | 89 ++- open-api/immich-openapi-specs.json | 597 ++++++++++++------ open-api/typescript-sdk/src/fetch-client.ts | 522 ++++++++++++++- server/src/enum.ts | 5 + server/src/middleware/auth.guard.ts | 12 +- server/src/utils/misc.ts | 34 +- 34 files changed, 1918 insertions(+), 428 deletions(-) diff --git a/mobile/openapi/lib/api/activities_api.dart b/mobile/openapi/lib/api/activities_api.dart index 5c83ba7db9..67015499fa 100644 --- a/mobile/openapi/lib/api/activities_api.dart +++ b/mobile/openapi/lib/api/activities_api.dart @@ -16,7 +16,10 @@ class ActivitiesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -45,6 +48,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.create` permission. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -63,7 +68,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'DELETE /activities/{id}' operation and returns the [Response]. + /// This endpoint requires the `activity.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class ActivitiesApi { } } - /// Performs an HTTP 'GET /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -154,6 +167,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.read` permission. + /// /// Parameters: /// /// * [String] albumId (required): @@ -183,7 +198,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'GET /activities/statistics' operation and returns the [Response]. + /// This endpoint requires the `activity.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -219,6 +237,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.statistics` permission. + /// /// Parameters: /// /// * [String] albumId (required): diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index fa7a562adb..10674b894f 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -16,7 +16,10 @@ class AlbumsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -59,6 +62,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -86,7 +91,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/users' operation and returns the [Response]. + /// This endpoint requires the `albumUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -118,6 +126,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -138,7 +148,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'POST /albums' operation and returns the [Response]. + /// This endpoint requires the `album.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -167,6 +180,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.create` permission. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -185,7 +200,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -215,6 +233,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -225,7 +245,10 @@ class AlbumsApi { } } - /// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -271,6 +294,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -295,7 +320,9 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums/statistics' operation and returns the [Response]. + /// This endpoint requires the `album.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAlbumStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/albums/statistics'; @@ -321,6 +348,7 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.statistics` permission. Future getAlbumStatistics() async { final response = await getAlbumStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +364,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] assetId: @@ -375,6 +406,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] assetId: @@ -399,7 +432,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -431,6 +467,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -454,7 +492,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -487,6 +528,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -499,7 +542,10 @@ class AlbumsApi { } } - /// Performs an HTTP 'PATCH /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -531,6 +577,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -551,7 +599,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -586,6 +637,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index cf54ac5c04..e86c63bc6e 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -16,7 +16,10 @@ class APIKeysApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -45,6 +48,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.create` permission. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -63,7 +68,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'DELETE /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class APIKeysApi { } } - /// Performs an HTTP 'GET /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -133,6 +146,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,9 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'GET /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApiKeysWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -177,6 +194,7 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. Future?> getApiKeys() async { final response = await getApiKeysWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -195,7 +213,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'PUT /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -227,6 +248,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index 3cb62785be..c0de1a0801 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -128,7 +128,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'DELETE /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -157,6 +160,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -167,7 +172,10 @@ class AssetsApi { } } - /// Performs an HTTP 'GET /assets/{id}/original' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -208,6 +216,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -290,7 +300,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -331,6 +344,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -353,7 +368,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -396,6 +414,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -418,7 +438,7 @@ class AssetsApi { return null; } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -454,7 +474,7 @@ class AssetsApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// @@ -477,7 +497,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/video/playback' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -518,6 +541,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -542,7 +567,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -636,7 +661,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Parameters: /// @@ -713,7 +738,10 @@ class AssetsApi { } } - /// Performs an HTTP 'PUT /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -745,6 +773,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -765,7 +795,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'PUT /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -794,6 +827,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -804,7 +839,10 @@ class AssetsApi { } } - /// Performs an HTTP 'POST /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.upload` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -922,6 +960,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.upload` permission. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -967,7 +1007,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -1013,6 +1056,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index 5482a9fc51..a74af33a43 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -16,7 +16,10 @@ class AuthenticationApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response]. + /// This endpoint requires the `auth.changePassword` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -45,6 +48,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `auth.changePassword` permission. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -63,7 +68,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'PUT /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -92,6 +100,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.update` permission. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -264,7 +274,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -293,6 +306,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.delete` permission. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -303,7 +318,10 @@ class AuthenticationApi { } } - /// Performs an HTTP 'POST /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): @@ -332,6 +350,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.create` permission. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index 7aa9662c23..f9a496b990 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -16,7 +16,7 @@ class DeprecatedApi { final ApiClient apiClient; - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -52,7 +52,7 @@ class DeprecatedApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 675996f932..62c97bfc9c 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -16,7 +16,10 @@ class DownloadApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /download/archive' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): @@ -56,6 +59,8 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): @@ -78,7 +83,10 @@ class DownloadApi { return null; } - /// Performs an HTTP 'POST /download/info' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): @@ -118,6 +126,8 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): diff --git a/mobile/openapi/lib/api/duplicates_api.dart b/mobile/openapi/lib/api/duplicates_api.dart index d8b45d21a2..9df6e46586 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -16,7 +16,10 @@ class DuplicatesApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class DuplicatesApi { } } - /// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -85,6 +93,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -95,7 +105,9 @@ class DuplicatesApi { } } - /// Performs an HTTP 'GET /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetDuplicatesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -121,6 +133,7 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.read` permission. Future?> getAssetDuplicates() async { final response = await getAssetDuplicatesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/faces_api.dart b/mobile/openapi/lib/api/faces_api.dart index 44e3d53f8e..2f8e6be60d 100644 --- a/mobile/openapi/lib/api/faces_api.dart +++ b/mobile/openapi/lib/api/faces_api.dart @@ -16,7 +16,10 @@ class FacesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /faces' operation and returns the [Response]. + /// This endpoint requires the `face.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -45,6 +48,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.create` permission. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -55,7 +60,10 @@ class FacesApi { } } - /// Performs an HTTP 'DELETE /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -87,6 +95,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -99,7 +109,10 @@ class FacesApi { } } - /// Performs an HTTP 'GET /faces' operation and returns the [Response]. + /// This endpoint requires the `face.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -130,6 +143,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,10 @@ class FacesApi { return null; } - /// Performs an HTTP 'PUT /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -183,6 +201,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/jobs_api.dart b/mobile/openapi/lib/api/jobs_api.dart index 182bb14e4f..4c935828a0 100644 --- a/mobile/openapi/lib/api/jobs_api.dart +++ b/mobile/openapi/lib/api/jobs_api.dart @@ -16,7 +16,10 @@ class JobsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -45,6 +48,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -55,7 +60,9 @@ class JobsApi { } } - /// Performs an HTTP 'GET /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllJobsStatusWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -81,6 +88,7 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.read` permission. Future getAllJobsStatus() async { final response = await getAllJobsStatusWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class JobsApi { return null; } - /// Performs an HTTP 'PUT /jobs/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobName] id (required): @@ -128,6 +139,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobName] id (required): diff --git a/mobile/openapi/lib/api/libraries_api.dart b/mobile/openapi/lib/api/libraries_api.dart index 86acce76b4..9258f8e3eb 100644 --- a/mobile/openapi/lib/api/libraries_api.dart +++ b/mobile/openapi/lib/api/libraries_api.dart @@ -16,7 +16,10 @@ class LibrariesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -45,6 +48,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -63,7 +68,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'DELETE /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,9 @@ class LibrariesApi { } } - /// Performs an HTTP 'GET /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllLibrariesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -129,6 +141,7 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. Future?> getAllLibraries() async { final response = await getAllLibrariesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -147,7 +160,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -177,6 +193,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -195,7 +213,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -225,6 +246,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -243,7 +266,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'POST /libraries/{id}/scan' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -273,6 +299,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -283,7 +311,10 @@ class LibrariesApi { } } - /// Performs an HTTP 'PUT /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -315,6 +346,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/map_api.dart b/mobile/openapi/lib/api/map_api.dart index ffe72df453..da4f3dffcc 100644 --- a/mobile/openapi/lib/api/map_api.dart +++ b/mobile/openapi/lib/api/map_api.dart @@ -19,18 +19,18 @@ class MapApi { /// Performs an HTTP 'GET /map/markers' operation and returns the [Response]. /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { + Future getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/markers'; @@ -41,18 +41,18 @@ class MapApi { final headerParams = {}; final formParams = {}; - if (fileCreatedAfter != null) { - queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); - } - if (fileCreatedBefore != null) { - queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); - } if (isArchived != null) { queryParams.addAll(_queryParams('', 'isArchived', isArchived)); } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } + if (fileCreatedAfter != null) { + queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); + } + if (fileCreatedBefore != null) { + queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -76,19 +76,19 @@ class MapApi { /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { - final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); + Future?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { + final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/memories_api.dart b/mobile/openapi/lib/api/memories_api.dart index 9b62cce9c0..f9280101e6 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -16,7 +16,10 @@ class MemoriesApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -48,6 +51,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -71,7 +76,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'POST /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -100,6 +108,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.create` permission. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -118,7 +128,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -148,6 +161,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -158,7 +173,10 @@ class MemoriesApi { } } - /// Performs an HTTP 'GET /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -206,7 +226,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories/statistics' operation and returns the [Response]. + /// This endpoint requires the `memory.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -254,6 +277,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.statistics` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -278,7 +303,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -310,6 +338,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -333,7 +363,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -381,6 +414,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -408,7 +443,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'PUT /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -440,6 +478,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 501cc70a29..1d276efaaf 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,7 +16,10 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -85,6 +93,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -95,7 +105,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -125,6 +138,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -143,7 +158,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -191,6 +209,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -218,7 +238,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -250,6 +273,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -270,7 +295,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): @@ -299,6 +327,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): diff --git a/mobile/openapi/lib/api/partners_api.dart b/mobile/openapi/lib/api/partners_api.dart index 9f10ea4d1e..eb5d5f5806 100644 --- a/mobile/openapi/lib/api/partners_api.dart +++ b/mobile/openapi/lib/api/partners_api.dart @@ -16,7 +16,10 @@ class PartnersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -64,7 +69,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'GET /partners' operation and returns the [Response]. + /// This endpoint requires the `partner.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -95,6 +103,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.read` permission. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -116,7 +126,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'DELETE /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -146,6 +159,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -156,7 +171,10 @@ class PartnersApi { } } - /// Performs an HTTP 'PUT /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/people_api.dart b/mobile/openapi/lib/api/people_api.dart index 35dbac4e97..68c16785cc 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -16,7 +16,10 @@ class PeopleApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /people' operation and returns the [Response]. + /// This endpoint requires the `person.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -45,6 +48,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.create` permission. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -63,7 +68,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'DELETE /people' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -92,6 +100,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -102,7 +112,10 @@ class PeopleApi { } } - /// Performs an HTTP 'DELETE /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -132,6 +145,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -142,7 +157,10 @@ class PeopleApi { } } - /// Performs an HTTP 'GET /people' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -197,6 +215,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -225,7 +245,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -255,6 +278,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -273,7 +298,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/statistics' operation and returns the [Response]. + /// This endpoint requires the `person.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -303,6 +331,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -321,7 +351,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -351,6 +384,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -369,7 +404,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'POST /people/{id}/merge' operation and returns the [Response]. + /// This endpoint requires the `person.merge` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -401,6 +439,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.merge` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -424,7 +464,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}/reassign' operation and returns the [Response]. + /// This endpoint requires the `person.reassign` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -456,6 +499,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.reassign` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -479,7 +524,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -508,6 +556,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -529,7 +579,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -561,6 +614,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 1b58702c40..4d9e1172b8 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -16,7 +16,9 @@ class SearchApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /search/cities' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetsByCityWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/cities'; @@ -42,6 +44,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getAssetsByCity() async { final response = await getAssetsByCityWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -60,7 +63,9 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/explore' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getExploreDataWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/explore'; @@ -86,6 +91,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getExploreData() async { final response = await getExploreDataWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -104,7 +110,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/suggestions' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -161,6 +170,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -193,7 +204,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -222,6 +236,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -240,7 +256,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/metadata' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -269,6 +288,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -287,7 +308,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/large-assets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [List] albumIds: @@ -470,6 +494,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [List] albumIds: @@ -551,7 +577,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -587,6 +616,8 @@ class SearchApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -610,7 +641,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/places' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -641,6 +675,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -662,7 +698,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/random' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -691,6 +730,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -712,7 +753,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/smart' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): @@ -741,6 +785,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 7abdabcd3e..9e250b83b5 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -16,7 +16,9 @@ class ServerApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -42,6 +44,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. Future deleteServerLicense() async { final response = await deleteServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -49,7 +52,9 @@ class ServerApi { } } - /// Performs an HTTP 'GET /server/about' operation and returns the [Response]. + /// This endpoint requires the `server.about` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAboutInfoWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/about'; @@ -75,6 +80,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.about` permission. Future getAboutInfo() async { final response = await getAboutInfoWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -90,7 +96,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/apk-links' operation and returns the [Response]. + /// This endpoint requires the `server.apkLinks` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApkLinksWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/apk-links'; @@ -116,6 +124,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.apkLinks` permission. Future getApkLinks() async { final response = await getApkLinksWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +222,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -239,6 +250,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. Future getServerLicense() async { final response = await getServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +266,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/statistics'; @@ -280,6 +294,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. Future getServerStatistics() async { final response = await getServerStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +351,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/storage' operation and returns the [Response]. + /// This endpoint requires the `server.storage` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/storage'; @@ -362,6 +379,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.storage` permission. Future getStorage() async { final response = await getStorageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -585,7 +603,10 @@ class ServerApi { return null; } - /// Performs an HTTP 'PUT /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -614,6 +635,8 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index d54f520641..63528d17a7 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -16,7 +16,10 @@ class SessionsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -45,6 +48,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.create` permission. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -63,7 +68,9 @@ class SessionsApi { return null; } - /// Performs an HTTP 'DELETE /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteAllSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -89,6 +96,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. Future deleteAllSessions() async { final response = await deleteAllSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class SessionsApi { } } - /// Performs an HTTP 'DELETE /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -126,6 +137,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -136,7 +149,9 @@ class SessionsApi { } } - /// Performs an HTTP 'GET /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -162,6 +177,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.read` permission. Future?> getSessions() async { final response = await getSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -180,7 +196,10 @@ class SessionsApi { return null; } - /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. + /// This endpoint requires the `session.lock` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -210,6 +229,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.lock` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -220,7 +241,10 @@ class SessionsApi { } } - /// Performs an HTTP 'PUT /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -252,6 +276,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index dd372b962b..e32c566754 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -86,7 +86,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'POST /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -115,6 +118,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.create` permission. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -133,7 +138,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -166,6 +174,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -190,14 +200,14 @@ class SharedLinksApi { /// Performs an HTTP 'GET /shared-links/me' operation and returns the [Response]. /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// - /// * [String] slug: - /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? slug, String? token, }) async { + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -208,18 +218,18 @@ class SharedLinksApi { final headerParams = {}; final formParams = {}; - if (key != null) { - queryParams.addAll(_queryParams('', 'key', key)); - } if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } - if (slug != null) { - queryParams.addAll(_queryParams('', 'slug', slug)); - } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -237,15 +247,15 @@ class SharedLinksApi { /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// - /// * [String] slug: - /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? slug, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, slug: slug, token: token, ); + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLink({ String? password, String? token, String? key, String? slug, }) async { + final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -259,7 +269,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -289,6 +302,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -307,7 +322,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'DELETE /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -337,6 +355,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -417,7 +437,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'PATCH /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -449,6 +472,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 6d6c4506be..0f76f3396b 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -16,7 +16,10 @@ class StacksApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -45,6 +48,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.create` permission. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -63,7 +68,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class StacksApi { } } - /// Performs an HTTP 'DELETE /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -132,6 +145,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -142,7 +157,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -172,6 +190,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -190,7 +210,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}/assets/{assetId}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] assetId (required): @@ -223,6 +246,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] assetId (required): @@ -235,7 +260,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -268,6 +296,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -289,7 +319,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'PUT /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -321,6 +354,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart index fe2876ddd8..9e594d6ace 100644 --- a/mobile/openapi/lib/api/sync_api.dart +++ b/mobile/openapi/lib/api/sync_api.dart @@ -16,7 +16,10 @@ class SyncApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -45,6 +48,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -152,7 +157,9 @@ class SyncApi { return null; } - /// Performs an HTTP 'GET /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSyncAckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -178,6 +185,7 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.read` permission. Future?> getSyncAck() async { final response = await getSyncAckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -196,7 +204,10 @@ class SyncApi { return null; } - /// Performs an HTTP 'POST /sync/stream' operation and returns the [Response]. + /// This endpoint requires the `sync.stream` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -225,6 +236,8 @@ class SyncApi { ); } + /// This endpoint requires the `sync.stream` permission. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -235,7 +248,10 @@ class SyncApi { } } - /// Performs an HTTP 'POST /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): @@ -264,6 +280,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.update` permission. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): diff --git a/mobile/openapi/lib/api/system_config_api.dart b/mobile/openapi/lib/api/system_config_api.dart index a03b9d3e72..2ab3879b8a 100644 --- a/mobile/openapi/lib/api/system_config_api.dart +++ b/mobile/openapi/lib/api/system_config_api.dart @@ -16,7 +16,9 @@ class SystemConfigApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -42,6 +44,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfig() async { final response = await getConfigWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/defaults' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigDefaultsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/defaults'; @@ -83,6 +88,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfigDefaults() async { final response = await getConfigDefaultsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/storage-template-options' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageTemplateOptionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/storage-template-options'; @@ -124,6 +132,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getStorageTemplateOptions() async { final response = await getStorageTemplateOptionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'PUT /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): @@ -168,6 +180,8 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 3fcceb8e42..f6b9bad1d6 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -16,7 +16,9 @@ class SystemMetadataApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAdminOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -42,6 +44,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getAdminOnboarding() async { final response = await getAdminOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/reverse-geocoding-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getReverseGeocodingStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/reverse-geocoding-state'; @@ -83,6 +88,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getReverseGeocodingState() async { final response = await getReverseGeocodingStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/version-check-state'; @@ -124,6 +132,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getVersionCheckState() async { final response = await getVersionCheckStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): @@ -168,6 +180,8 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index f6cfc8720b..a0cdb91acf 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -16,7 +16,10 @@ class TagsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /tags/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -45,6 +48,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -63,7 +68,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'POST /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -92,6 +100,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -110,7 +120,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -140,6 +153,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -150,7 +165,9 @@ class TagsApi { } } - /// Performs an HTTP 'GET /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllTagsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -176,6 +193,7 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. Future?> getAllTags() async { final response = await getAllTagsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -194,7 +212,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -224,6 +245,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -242,7 +265,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -274,6 +300,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -297,7 +325,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -329,6 +360,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -352,7 +385,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -384,6 +420,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -404,7 +442,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): @@ -433,6 +474,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 2d3ced610b..2d142e3d67 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -16,7 +16,10 @@ class TimelineApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /timeline/bucket' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -118,6 +121,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -171,7 +176,10 @@ class TimelineApi { return null; } - /// Performs an HTTP 'GET /timeline/buckets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -269,6 +277,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] albumId: diff --git a/mobile/openapi/lib/api/trash_api.dart b/mobile/openapi/lib/api/trash_api.dart index 982dbcbeda..480d19960a 100644 --- a/mobile/openapi/lib/api/trash_api.dart +++ b/mobile/openapi/lib/api/trash_api.dart @@ -16,7 +16,9 @@ class TrashApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /trash/empty' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future emptyTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/empty'; @@ -42,6 +44,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future emptyTrash() async { final response = await emptyTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,10 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore/assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -86,6 +92,8 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -104,7 +112,9 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future restoreTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore'; @@ -130,6 +140,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future restoreTrash() async { final response = await restoreTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index 58263504ce..e4fc1673ef 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -16,7 +16,10 @@ class UsersAdminApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -45,6 +48,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -63,7 +68,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -95,6 +103,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -115,7 +125,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -145,6 +158,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -163,7 +178,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -193,6 +211,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -211,7 +231,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -257,6 +280,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -281,7 +306,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -311,6 +339,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -329,7 +359,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -367,6 +400,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -390,7 +425,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -422,6 +460,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -442,7 +482,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -474,6 +517,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/users_api.dart b/mobile/openapi/lib/api/users_api.dart index cd31617e74..c8891ba0c2 100644 --- a/mobile/openapi/lib/api/users_api.dart +++ b/mobile/openapi/lib/api/users_api.dart @@ -16,7 +16,10 @@ class UsersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -55,6 +58,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.update` permission. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -73,7 +78,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'DELETE /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteProfileImageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -99,6 +106,7 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.delete` permission. Future deleteProfileImage() async { final response = await deleteProfileImageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -106,7 +114,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -132,6 +142,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.delete` permission. Future deleteUserLicense() async { final response = await deleteUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +150,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -165,6 +178,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.delete` permission. Future deleteUserOnboarding() async { final response = await deleteUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -172,7 +186,9 @@ class UsersApi { } } - /// Performs an HTTP 'GET /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyPreferencesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -198,6 +214,7 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.read` permission. Future getMyPreferences() async { final response = await getMyPreferencesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +230,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyUserWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -239,6 +258,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future getMyUser() async { final response = await getMyUserWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +274,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -284,6 +307,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -302,7 +327,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -332,6 +360,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -350,7 +380,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -376,6 +408,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.read` permission. Future getUserLicense() async { final response = await getUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -391,7 +424,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -417,6 +452,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.read` permission. Future getUserOnboarding() async { final response = await getUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -432,7 +468,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future searchUsersWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users'; @@ -458,6 +496,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future?> searchUsers() async { final response = await searchUsersWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -476,7 +515,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -505,6 +547,8 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -523,7 +567,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -552,6 +599,8 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.update` permission. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -570,7 +619,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -599,6 +651,8 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.update` permission. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -617,7 +671,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): @@ -646,6 +703,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.update` permission. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 8c491ca471..9a1e6a6937 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -78,7 +78,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.read" + "x-immich-permission": "activity.read", + "description": "This endpoint requires the `activity.read` permission." }, "post": { "operationId": "createActivity", @@ -119,7 +120,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.create" + "x-immich-permission": "activity.create", + "description": "This endpoint requires the `activity.create` permission." } }, "/activities/statistics": { @@ -171,7 +173,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.statistics" + "x-immich-permission": "activity.statistics", + "description": "This endpoint requires the `activity.statistics` permission." } }, "/activities/{id}": { @@ -207,7 +210,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.delete" + "x-immich-permission": "activity.delete", + "description": "This endpoint requires the `activity.delete` permission." } }, "/admin/notifications": { @@ -249,7 +253,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/templates/{name}": { @@ -300,7 +305,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/test-email": { @@ -342,7 +348,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/users": { @@ -396,7 +403,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "post": { "operationId": "createUserAdmin", @@ -437,7 +446,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.create" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.create", + "description": "This endpoint is an admin-only route, and requires the `adminUser.create` permission." } }, "/admin/users/{id}": { @@ -490,7 +501,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.delete" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." }, "get": { "operationId": "getUserAdmin", @@ -531,7 +544,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserAdmin", @@ -582,7 +597,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.update" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/preferences": { @@ -625,7 +642,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserPreferencesAdmin", @@ -676,7 +695,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.update" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/restore": { @@ -719,7 +740,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.delete" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." } }, "/admin/users/{id}/statistics": { @@ -786,7 +809,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." } }, "/albums": { @@ -841,7 +866,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.read" + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "post": { "operationId": "createAlbum", @@ -882,7 +908,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.create" + "x-immich-permission": "album.create", + "description": "This endpoint requires the `album.create` permission." } }, "/albums/statistics": { @@ -915,7 +942,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.statistics" + "x-immich-permission": "album.statistics", + "description": "This endpoint requires the `album.statistics` permission." } }, "/albums/{id}": { @@ -951,7 +979,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.delete" + "x-immich-permission": "album.delete", + "description": "This endpoint requires the `album.delete` permission." }, "get": { "operationId": "getAlbumInfo", @@ -1016,7 +1045,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.read" + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "patch": { "operationId": "updateAlbumInfo", @@ -1067,7 +1097,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.update" + "x-immich-permission": "album.update", + "description": "This endpoint requires the `album.update` permission." } }, "/albums/{id}/assets": { @@ -1123,7 +1154,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumAsset.delete" + "x-immich-permission": "albumAsset.delete", + "description": "This endpoint requires the `albumAsset.delete` permission." }, "put": { "operationId": "addAssetsToAlbum", @@ -1193,7 +1225,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumAsset.create" + "x-immich-permission": "albumAsset.create", + "description": "This endpoint requires the `albumAsset.create` permission." } }, "/albums/{id}/user/{userId}": { @@ -1237,7 +1270,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.delete" + "x-immich-permission": "albumUser.delete", + "description": "This endpoint requires the `albumUser.delete` permission." }, "put": { "operationId": "updateAlbumUser", @@ -1289,7 +1323,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.update" + "x-immich-permission": "albumUser.update", + "description": "This endpoint requires the `albumUser.update` permission." } }, "/albums/{id}/users": { @@ -1342,7 +1377,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.create" + "x-immich-permission": "albumUser.create", + "description": "This endpoint requires the `albumUser.create` permission." } }, "/api-keys": { @@ -1378,7 +1414,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.read" + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "post": { "operationId": "createApiKey", @@ -1419,7 +1456,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.create" + "x-immich-permission": "apiKey.create", + "description": "This endpoint requires the `apiKey.create` permission." } }, "/api-keys/{id}": { @@ -1455,7 +1493,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.delete" + "x-immich-permission": "apiKey.delete", + "description": "This endpoint requires the `apiKey.delete` permission." }, "get": { "operationId": "getApiKey", @@ -1496,7 +1535,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.read" + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "put": { "operationId": "updateApiKey", @@ -1547,7 +1587,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.update" + "x-immich-permission": "apiKey.update", + "description": "This endpoint requires the `apiKey.update` permission." } }, "/assets": { @@ -1583,7 +1624,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." }, "post": { "operationId": "uploadAsset", @@ -1651,7 +1693,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.upload" + "x-immich-permission": "asset.upload", + "description": "This endpoint requires the `asset.upload` permission." }, "put": { "operationId": "updateAssets", @@ -1685,7 +1728,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.update" + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/bulk-upload-check": { @@ -1860,7 +1904,7 @@ "/assets/random": { "get": { "deprecated": true, - "description": "This property was deprecated in v1.116.0", + "description": "This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission.", "operationId": "getRandom", "parameters": [ { @@ -1964,7 +2008,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.statistics" + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/assets/{id}": { @@ -2023,7 +2068,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." }, "put": { "operationId": "updateAsset", @@ -2074,7 +2120,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.update" + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/{id}/original": { @@ -2134,10 +2181,11 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." }, "put": { - "description": "Replace the asset with new file, without changing its id", + "description": "Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission.", "operationId": "replaceAsset", "parameters": [ { @@ -2274,7 +2322,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.view" + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." } }, "/assets/{id}/video/playback": { @@ -2334,7 +2383,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.view" + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." } }, "/auth/admin-sign-up": { @@ -2408,7 +2458,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "auth.changePassword" + "x-immich-permission": "auth.changePassword", + "description": "This endpoint requires the `auth.changePassword` permission." } }, "/auth/login": { @@ -2507,7 +2558,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.delete" + "x-immich-permission": "pinCode.delete", + "description": "This endpoint requires the `pinCode.delete` permission." }, "post": { "operationId": "setupPinCode", @@ -2541,7 +2593,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.create" + "x-immich-permission": "pinCode.create", + "description": "This endpoint requires the `pinCode.create` permission." }, "put": { "operationId": "changePinCode", @@ -2575,7 +2628,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.update" + "x-immich-permission": "pinCode.update", + "description": "This endpoint requires the `pinCode.update` permission." } }, "/auth/session/lock": { @@ -2760,7 +2814,8 @@ "tags": [ "Download" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/download/info": { @@ -2820,7 +2875,8 @@ "tags": [ "Download" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/duplicates": { @@ -2856,7 +2912,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.delete" + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." }, "get": { "operationId": "getAssetDuplicates", @@ -2890,7 +2947,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.read" + "x-immich-permission": "duplicate.read", + "description": "This endpoint requires the `duplicate.read` permission." } }, "/duplicates/{id}": { @@ -2926,7 +2984,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.delete" + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." } }, "/faces": { @@ -2972,7 +3031,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.read" + "x-immich-permission": "face.read", + "description": "This endpoint requires the `face.read` permission." }, "post": { "operationId": "createFace", @@ -3006,7 +3066,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.create" + "x-immich-permission": "face.create", + "description": "This endpoint requires the `face.create` permission." } }, "/faces/{id}": { @@ -3052,7 +3113,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.delete" + "x-immich-permission": "face.delete", + "description": "This endpoint requires the `face.delete` permission." }, "put": { "operationId": "reassignFacesById", @@ -3103,7 +3165,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.update" + "x-immich-permission": "face.update", + "description": "This endpoint requires the `face.update` permission." } }, "/jobs": { @@ -3136,7 +3199,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.read" + "x-immich-admin-only": true, + "x-immich-permission": "job.read", + "description": "This endpoint is an admin-only route, and requires the `job.read` permission." }, "post": { "operationId": "createJob", @@ -3170,7 +3235,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.create" + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/jobs/{id}": { @@ -3222,7 +3289,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.create" + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/libraries": { @@ -3258,7 +3327,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.read" + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "post": { "operationId": "createLibrary", @@ -3299,7 +3370,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.create" + "x-immich-admin-only": true, + "x-immich-permission": "library.create", + "description": "This endpoint is an admin-only route, and requires the `library.create` permission." } }, "/libraries/{id}": { @@ -3335,7 +3408,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.delete" + "x-immich-admin-only": true, + "x-immich-permission": "library.delete", + "description": "This endpoint is an admin-only route, and requires the `library.delete` permission." }, "get": { "operationId": "getLibrary", @@ -3376,7 +3451,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.read" + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "put": { "operationId": "updateLibrary", @@ -3427,7 +3504,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.update" + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/scan": { @@ -3463,7 +3542,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.update" + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/statistics": { @@ -3506,7 +3587,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.statistics" + "x-immich-admin-only": true, + "x-immich-permission": "library.statistics", + "description": "This endpoint is an admin-only route, and requires the `library.statistics` permission." } }, "/libraries/{id}/validate": { @@ -3558,13 +3641,30 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true } }, "/map/markers": { "get": { "operationId": "getMapMarkers", "parameters": [ + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, { "name": "fileCreatedAfter", "required": false, @@ -3583,22 +3683,6 @@ "type": "string" } }, - { - "name": "isArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isFavorite", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, { "name": "withPartners", "required": false, @@ -3768,7 +3852,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.read" + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "post": { "operationId": "createMemory", @@ -3809,7 +3894,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.create" + "x-immich-permission": "memory.create", + "description": "This endpoint requires the `memory.create` permission." } }, "/memories/statistics": { @@ -3876,7 +3962,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.statistics" + "x-immich-permission": "memory.statistics", + "description": "This endpoint requires the `memory.statistics` permission." } }, "/memories/{id}": { @@ -3912,7 +3999,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.delete" + "x-immich-permission": "memory.delete", + "description": "This endpoint requires the `memory.delete` permission." }, "get": { "operationId": "getMemory", @@ -3953,7 +4041,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.read" + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "put": { "operationId": "updateMemory", @@ -4004,7 +4093,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.update" + "x-immich-permission": "memory.update", + "description": "This endpoint requires the `memory.update` permission." } }, "/memories/{id}/assets": { @@ -4060,7 +4150,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memoryAsset.delete" + "x-immich-permission": "memoryAsset.delete", + "description": "This endpoint requires the `memoryAsset.delete` permission." }, "put": { "operationId": "addMemoryAssets", @@ -4114,7 +4205,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memoryAsset.create" + "x-immich-permission": "memoryAsset.create", + "description": "This endpoint requires the `memoryAsset.create` permission." } }, "/notifications": { @@ -4150,7 +4242,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.delete" + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotifications", @@ -4218,7 +4311,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.read" + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotifications", @@ -4252,7 +4346,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.update" + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/notifications/{id}": { @@ -4288,7 +4383,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.delete" + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotification", @@ -4329,7 +4425,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.read" + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotification", @@ -4380,7 +4477,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.update" + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/oauth/authorize": { @@ -4575,7 +4673,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.read" + "x-immich-permission": "partner.read", + "description": "This endpoint requires the `partner.read` permission." } }, "/partners/{id}": { @@ -4611,7 +4710,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.delete" + "x-immich-permission": "partner.delete", + "description": "This endpoint requires the `partner.delete` permission." }, "post": { "operationId": "createPartner", @@ -4652,7 +4752,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.create" + "x-immich-permission": "partner.create", + "description": "This endpoint requires the `partner.create` permission." }, "put": { "operationId": "updatePartner", @@ -4703,7 +4804,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.update" + "x-immich-permission": "partner.update", + "description": "This endpoint requires the `partner.update` permission." } }, "/people": { @@ -4739,7 +4841,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.delete" + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getAllPeople", @@ -4820,7 +4923,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "post": { "operationId": "createPerson", @@ -4861,7 +4965,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.create" + "x-immich-permission": "person.create", + "description": "This endpoint requires the `person.create` permission." }, "put": { "operationId": "updatePeople", @@ -4905,7 +5010,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.update" + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}": { @@ -4941,7 +5047,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.delete" + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getPerson", @@ -4982,7 +5089,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "put": { "operationId": "updatePerson", @@ -5033,7 +5141,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.update" + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}/merge": { @@ -5089,7 +5198,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.merge" + "x-immich-permission": "person.merge", + "description": "This endpoint requires the `person.merge` permission." } }, "/people/{id}/reassign": { @@ -5145,7 +5255,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.reassign" + "x-immich-permission": "person.reassign", + "description": "This endpoint requires the `person.reassign` permission." } }, "/people/{id}/statistics": { @@ -5188,7 +5299,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.statistics" + "x-immich-permission": "person.statistics", + "description": "This endpoint requires the `person.statistics` permission." } }, "/people/{id}/thumbnail": { @@ -5232,7 +5344,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/cities": { @@ -5268,7 +5381,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/explore": { @@ -5304,7 +5418,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/large-assets": { @@ -5622,7 +5737,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/metadata": { @@ -5665,7 +5781,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/person": { @@ -5718,7 +5835,8 @@ "tags": [ "Search" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/places": { @@ -5763,7 +5881,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/random": { @@ -5809,7 +5928,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/smart": { @@ -5852,7 +5972,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/statistics": { @@ -5895,7 +6016,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.statistics" + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/search/suggestions": { @@ -5981,7 +6103,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/server/about": { @@ -6014,7 +6137,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.about" + "x-immich-permission": "server.about", + "description": "This endpoint requires the `server.about` permission." } }, "/server/apk-links": { @@ -6047,7 +6171,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.apkLinks" + "x-immich-permission": "server.apkLinks", + "description": "This endpoint requires the `server.apkLinks` permission." } }, "/server/config": { @@ -6115,7 +6240,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.delete" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.delete", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.delete` permission." }, "get": { "operationId": "getServerLicense", @@ -6149,7 +6276,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.read" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.read", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.read` permission." }, "put": { "operationId": "setServerLicense", @@ -6190,7 +6319,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.update" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.update", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.update` permission." } }, "/server/media-types": { @@ -6265,7 +6396,9 @@ "tags": [ "Server" ], - "x-immich-permission": "server.statistics" + "x-immich-admin-only": true, + "x-immich-permission": "server.statistics", + "description": "This endpoint is an admin-only route, and requires the `server.statistics` permission." } }, "/server/storage": { @@ -6298,7 +6431,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.storage" + "x-immich-permission": "server.storage", + "description": "This endpoint requires the `server.storage` permission." } }, "/server/theme": { @@ -6422,7 +6556,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.delete" + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "get": { "operationId": "getSessions", @@ -6456,7 +6591,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.read" + "x-immich-permission": "session.read", + "description": "This endpoint requires the `session.read` permission." }, "post": { "operationId": "createSession", @@ -6497,7 +6633,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.create" + "x-immich-permission": "session.create", + "description": "This endpoint requires the `session.create` permission." } }, "/sessions/{id}": { @@ -6533,7 +6670,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.delete" + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "put": { "operationId": "updateSession", @@ -6584,7 +6722,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.update" + "x-immich-permission": "session.update", + "description": "This endpoint requires the `session.update` permission." } }, "/sessions/{id}/lock": { @@ -6620,7 +6759,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.lock" + "x-immich-permission": "session.lock", + "description": "This endpoint requires the `session.lock` permission." } }, "/shared-links": { @@ -6666,7 +6806,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.read" + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "post": { "operationId": "createSharedLink", @@ -6707,21 +6848,14 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.create" + "x-immich-permission": "sharedLink.create", + "description": "This endpoint requires the `sharedLink.create` permission." } }, "/shared-links/me": { "get": { "operationId": "getMySharedLink", "parameters": [ - { - "name": "key", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "password", "required": false, @@ -6732,7 +6866,7 @@ } }, { - "name": "slug", + "name": "token", "required": false, "in": "query", "schema": { @@ -6740,7 +6874,15 @@ } }, { - "name": "token", + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", "required": false, "in": "query", "schema": { @@ -6809,7 +6951,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.delete" + "x-immich-permission": "sharedLink.delete", + "description": "This endpoint requires the `sharedLink.delete` permission." }, "get": { "operationId": "getSharedLinkById", @@ -6850,7 +6993,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.read" + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "patch": { "operationId": "updateSharedLink", @@ -6901,7 +7045,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.update" + "x-immich-permission": "sharedLink.update", + "description": "This endpoint requires the `sharedLink.update` permission." } }, "/shared-links/{id}/assets": { @@ -7077,7 +7222,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.delete" + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "searchStacks", @@ -7121,7 +7267,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.read" + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "post": { "operationId": "createStack", @@ -7162,7 +7309,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.create" + "x-immich-permission": "stack.create", + "description": "This endpoint requires the `stack.create` permission." } }, "/stacks/{id}": { @@ -7198,7 +7346,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.delete" + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "getStack", @@ -7239,7 +7388,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.read" + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "put": { "operationId": "updateStack", @@ -7290,7 +7440,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.update" + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." } }, "/stacks/{id}/assets/{assetId}": { @@ -7335,7 +7486,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.update" + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." } }, "/sync/ack": { @@ -7371,7 +7523,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.delete" + "x-immich-permission": "syncCheckpoint.delete", + "description": "This endpoint requires the `syncCheckpoint.delete` permission." }, "get": { "operationId": "getSyncAck", @@ -7405,7 +7558,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.read" + "x-immich-permission": "syncCheckpoint.read", + "description": "This endpoint requires the `syncCheckpoint.read` permission." }, "post": { "operationId": "sendSyncAck", @@ -7439,7 +7593,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.update" + "x-immich-permission": "syncCheckpoint.update", + "description": "This endpoint requires the `syncCheckpoint.update` permission." } }, "/sync/delta-sync": { @@ -7562,7 +7717,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "sync.stream" + "x-immich-permission": "sync.stream", + "description": "This endpoint requires the `sync.stream` permission." } }, "/system-config": { @@ -7595,7 +7751,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." }, "put": { "operationId": "updateConfig", @@ -7636,7 +7794,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.update" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.update", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.update` permission." } }, "/system-config/defaults": { @@ -7669,7 +7829,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-config/storage-template-options": { @@ -7702,7 +7864,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-metadata/admin-onboarding": { @@ -7735,7 +7899,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." }, "post": { "operationId": "updateAdminOnboarding", @@ -7769,7 +7935,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.update" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.update", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.update` permission." } }, "/system-metadata/reverse-geocoding-state": { @@ -7802,7 +7970,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/system-metadata/version-check-state": { @@ -7835,7 +8005,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/tags": { @@ -7871,7 +8043,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.read" + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "post": { "operationId": "createTag", @@ -7912,7 +8085,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.create" + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." }, "put": { "operationId": "upsertTags", @@ -7956,7 +8130,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.create" + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." } }, "/tags/assets": { @@ -7999,7 +8174,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/tags/{id}": { @@ -8035,7 +8211,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.delete" + "x-immich-permission": "tag.delete", + "description": "This endpoint requires the `tag.delete` permission." }, "get": { "operationId": "getTagById", @@ -8076,7 +8253,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.read" + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "put": { "operationId": "updateTag", @@ -8127,7 +8305,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.update" + "x-immich-permission": "tag.update", + "description": "This endpoint requires the `tag.update` permission." } }, "/tags/{id}/assets": { @@ -8183,7 +8362,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." }, "put": { "operationId": "tagAssets", @@ -8237,7 +8417,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/timeline/bucket": { @@ -8391,7 +8572,8 @@ "tags": [ "Timeline" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/timeline/buckets": { @@ -8538,7 +8720,8 @@ "tags": [ "Timeline" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/trash/empty": { @@ -8571,7 +8754,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore": { @@ -8604,7 +8788,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore/assets": { @@ -8647,7 +8832,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/users": { @@ -8683,7 +8869,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/me": { @@ -8716,7 +8903,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." }, "put": { "operationId": "updateMyUser", @@ -8757,7 +8945,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.update" + "x-immich-permission": "user.update", + "description": "This endpoint requires the `user.update` permission." } }, "/users/me/license": { @@ -8783,7 +8972,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.delete" + "x-immich-permission": "userLicense.delete", + "description": "This endpoint requires the `userLicense.delete` permission." }, "get": { "operationId": "getUserLicense", @@ -8814,7 +9004,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.read" + "x-immich-permission": "userLicense.read", + "description": "This endpoint requires the `userLicense.read` permission." }, "put": { "operationId": "setUserLicense", @@ -8855,7 +9046,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.update" + "x-immich-permission": "userLicense.update", + "description": "This endpoint requires the `userLicense.update` permission." } }, "/users/me/onboarding": { @@ -8881,7 +9073,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.delete" + "x-immich-permission": "userOnboarding.delete", + "description": "This endpoint requires the `userOnboarding.delete` permission." }, "get": { "operationId": "getUserOnboarding", @@ -8912,7 +9105,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.read" + "x-immich-permission": "userOnboarding.read", + "description": "This endpoint requires the `userOnboarding.read` permission." }, "put": { "operationId": "setUserOnboarding", @@ -8953,7 +9147,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.update" + "x-immich-permission": "userOnboarding.update", + "description": "This endpoint requires the `userOnboarding.update` permission." } }, "/users/me/preferences": { @@ -8986,7 +9181,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userPreference.read" + "x-immich-permission": "userPreference.read", + "description": "This endpoint requires the `userPreference.read` permission." }, "put": { "operationId": "updateMyPreferences", @@ -9027,7 +9223,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userPreference.update" + "x-immich-permission": "userPreference.update", + "description": "This endpoint requires the `userPreference.update` permission." } }, "/users/profile-image": { @@ -9053,7 +9250,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.delete" + "x-immich-permission": "userProfileImage.delete", + "description": "This endpoint requires the `userProfileImage.delete` permission." }, "post": { "operationId": "createProfileImage", @@ -9095,7 +9293,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.update" + "x-immich-permission": "userProfileImage.update", + "description": "This endpoint requires the `userProfileImage.update` permission." } }, "/users/{id}": { @@ -9138,7 +9337,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/{id}/profile-image": { @@ -9182,7 +9382,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.read" + "x-immich-permission": "userProfileImage.read", + "description": "This endpoint requires the `userProfileImage.read` permission." } }, "/view/folder": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 33f0da8a08..53a2f4b144 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1578,6 +1578,9 @@ export type CreateProfileImageResponseDto = { profileImagePath: string; userId: string; }; +/** + * This endpoint requires the `activity.read` permission. + */ export function getActivities({ albumId, assetId, level, $type, userId }: { albumId: string; assetId?: string; @@ -1598,6 +1601,9 @@ export function getActivities({ albumId, assetId, level, $type, userId }: { ...opts })); } +/** + * This endpoint requires the `activity.create` permission. + */ export function createActivity({ activityCreateDto }: { activityCreateDto: ActivityCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1610,6 +1616,9 @@ export function createActivity({ activityCreateDto }: { body: activityCreateDto }))); } +/** + * This endpoint requires the `activity.statistics` permission. + */ export function getActivityStatistics({ albumId, assetId }: { albumId: string; assetId?: string; @@ -1624,6 +1633,9 @@ export function getActivityStatistics({ albumId, assetId }: { ...opts })); } +/** + * This endpoint requires the `activity.delete` permission. + */ export function deleteActivity({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1669,6 +1681,9 @@ export function sendTestEmailAdmin({ systemConfigSmtpDto }: { body: systemConfigSmtpDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function searchUsersAdmin({ id, withDeleted }: { id?: string; withDeleted?: boolean; @@ -1683,6 +1698,9 @@ export function searchUsersAdmin({ id, withDeleted }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.create` permission. + */ export function createUserAdmin({ userAdminCreateDto }: { userAdminCreateDto: UserAdminCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1695,6 +1713,9 @@ export function createUserAdmin({ userAdminCreateDto }: { body: userAdminCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function deleteUserAdmin({ id, userAdminDeleteDto }: { id: string; userAdminDeleteDto: UserAdminDeleteDto; @@ -1708,6 +1729,9 @@ export function deleteUserAdmin({ id, userAdminDeleteDto }: { body: userAdminDeleteDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1718,6 +1742,9 @@ export function getUserAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserAdmin({ id, userAdminUpdateDto }: { id: string; userAdminUpdateDto: UserAdminUpdateDto; @@ -1731,6 +1758,9 @@ export function updateUserAdmin({ id, userAdminUpdateDto }: { body: userAdminUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserPreferencesAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1741,6 +1771,9 @@ export function getUserPreferencesAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { id: string; userPreferencesUpdateDto: UserPreferencesUpdateDto; @@ -1754,6 +1787,9 @@ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function restoreUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1765,6 +1801,9 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility }: { id: string; isFavorite?: boolean; @@ -1782,6 +1821,9 @@ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility } ...opts })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAllAlbums({ assetId, shared }: { assetId?: string; shared?: boolean; @@ -1796,6 +1838,9 @@ export function getAllAlbums({ assetId, shared }: { ...opts })); } +/** + * This endpoint requires the `album.create` permission. + */ export function createAlbum({ createAlbumDto }: { createAlbumDto: CreateAlbumDto; }, opts?: Oazapfts.RequestOpts) { @@ -1808,6 +1853,9 @@ export function createAlbum({ createAlbumDto }: { body: createAlbumDto }))); } +/** + * This endpoint requires the `album.statistics` permission. + */ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1816,6 +1864,9 @@ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `album.delete` permission. + */ export function deleteAlbum({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1824,6 +1875,9 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; @@ -1841,6 +1895,9 @@ export function getAlbumInfo({ id, key, slug, withoutAssets }: { ...opts })); } +/** + * This endpoint requires the `album.update` permission. + */ export function updateAlbumInfo({ id, updateAlbumDto }: { id: string; updateAlbumDto: UpdateAlbumDto; @@ -1854,6 +1911,9 @@ export function updateAlbumInfo({ id, updateAlbumDto }: { body: updateAlbumDto }))); } +/** + * This endpoint requires the `albumAsset.delete` permission. + */ export function removeAssetFromAlbum({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -1867,6 +1927,9 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumAsset.create` permission. + */ export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; @@ -1885,6 +1948,9 @@ export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumUser.delete` permission. + */ export function removeUserFromAlbum({ id, userId }: { id: string; userId: string; @@ -1894,6 +1960,9 @@ export function removeUserFromAlbum({ id, userId }: { method: "DELETE" })); } +/** + * This endpoint requires the `albumUser.update` permission. + */ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { id: string; userId: string; @@ -1905,6 +1974,9 @@ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { body: updateAlbumUserDto }))); } +/** + * This endpoint requires the `albumUser.create` permission. + */ export function addUsersToAlbum({ id, addUsersDto }: { id: string; addUsersDto: AddUsersDto; @@ -1918,6 +1990,9 @@ export function addUsersToAlbum({ id, addUsersDto }: { body: addUsersDto }))); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKeys(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1926,6 +2001,9 @@ export function getApiKeys(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `apiKey.create` permission. + */ export function createApiKey({ apiKeyCreateDto }: { apiKeyCreateDto: ApiKeyCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1938,6 +2016,9 @@ export function createApiKey({ apiKeyCreateDto }: { body: apiKeyCreateDto }))); } +/** + * This endpoint requires the `apiKey.delete` permission. + */ export function deleteApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1946,6 +2027,9 @@ export function deleteApiKey({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1956,6 +2040,9 @@ export function getApiKey({ id }: { ...opts })); } +/** + * This endpoint requires the `apiKey.update` permission. + */ export function updateApiKey({ id, apiKeyUpdateDto }: { id: string; apiKeyUpdateDto: ApiKeyUpdateDto; @@ -1969,6 +2056,9 @@ export function updateApiKey({ id, apiKeyUpdateDto }: { body: apiKeyUpdateDto }))); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function deleteAssets({ assetBulkDeleteDto }: { assetBulkDeleteDto: AssetBulkDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -1978,6 +2068,9 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } +/** + * This endpoint requires the `asset.upload` permission. + */ export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; slug?: string; @@ -1999,6 +2092,9 @@ export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: }) }))); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAssets({ assetBulkUpdateDto }: { assetBulkUpdateDto: AssetBulkUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2061,7 +2157,7 @@ export function runAssetJobs({ assetJobsDto }: { }))); } /** - * This property was deprecated in v1.116.0 + * This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. */ export function getRandom({ count }: { count?: number; @@ -2075,6 +2171,9 @@ export function getRandom({ count }: { ...opts })); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { isFavorite?: boolean; isTrashed?: boolean; @@ -2091,6 +2190,9 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetInfo({ id, key, slug }: { id: string; key?: string; @@ -2106,6 +2208,9 @@ export function getAssetInfo({ id, key, slug }: { ...opts })); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAsset({ id, updateAssetDto }: { id: string; updateAssetDto: UpdateAssetDto; @@ -2119,6 +2224,9 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } +/** + * This endpoint requires the `asset.download` permission. + */ export function downloadAsset({ id, key, slug }: { id: string; key?: string; @@ -2155,6 +2263,9 @@ export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { body: assetMediaReplaceDto }))); } +/** + * This endpoint requires the `asset.view` permission. + */ export function viewAsset({ id, key, size, slug }: { id: string; key?: string; @@ -2172,6 +2283,9 @@ export function viewAsset({ id, key, size, slug }: { ...opts })); } +/** + * This endpoint requires the `asset.view` permission. + */ export function playAssetVideo({ id, key, slug }: { id: string; key?: string; @@ -2199,6 +2313,9 @@ export function signUpAdmin({ signUpDto }: { body: signUpDto }))); } +/** + * This endpoint requires the `auth.changePassword` permission. + */ export function changePassword({ changePasswordDto }: { changePasswordDto: ChangePasswordDto; }, opts?: Oazapfts.RequestOpts) { @@ -2232,6 +2349,9 @@ export function logout(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `pinCode.delete` permission. + */ export function resetPinCode({ pinCodeResetDto }: { pinCodeResetDto: PinCodeResetDto; }, opts?: Oazapfts.RequestOpts) { @@ -2241,6 +2361,9 @@ export function resetPinCode({ pinCodeResetDto }: { body: pinCodeResetDto }))); } +/** + * This endpoint requires the `pinCode.create` permission. + */ export function setupPinCode({ pinCodeSetupDto }: { pinCodeSetupDto: PinCodeSetupDto; }, opts?: Oazapfts.RequestOpts) { @@ -2250,6 +2373,9 @@ export function setupPinCode({ pinCodeSetupDto }: { body: pinCodeSetupDto }))); } +/** + * This endpoint requires the `pinCode.update` permission. + */ export function changePinCode({ pinCodeChangeDto }: { pinCodeChangeDto: PinCodeChangeDto; }, opts?: Oazapfts.RequestOpts) { @@ -2291,6 +2417,9 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.download` permission. + */ export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; slug?: string; @@ -2308,6 +2437,9 @@ export function downloadArchive({ key, slug, assetIdsDto }: { body: assetIdsDto }))); } +/** + * This endpoint requires the `asset.download` permission. + */ export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; slug?: string; @@ -2325,6 +2457,9 @@ export function getDownloadInfo({ key, slug, downloadInfoDto }: { body: downloadInfoDto }))); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicates({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2334,6 +2469,9 @@ export function deleteDuplicates({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `duplicate.read` permission. + */ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2342,6 +2480,9 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicate({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2350,6 +2491,9 @@ export function deleteDuplicate({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `face.read` permission. + */ export function getFaces({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2362,6 +2506,9 @@ export function getFaces({ id }: { ...opts })); } +/** + * This endpoint requires the `face.create` permission. + */ export function createFace({ assetFaceCreateDto }: { assetFaceCreateDto: AssetFaceCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2371,6 +2518,9 @@ export function createFace({ assetFaceCreateDto }: { body: assetFaceCreateDto }))); } +/** + * This endpoint requires the `face.delete` permission. + */ export function deleteFace({ id, assetFaceDeleteDto }: { id: string; assetFaceDeleteDto: AssetFaceDeleteDto; @@ -2381,6 +2531,9 @@ export function deleteFace({ id, assetFaceDeleteDto }: { body: assetFaceDeleteDto }))); } +/** + * This endpoint requires the `face.update` permission. + */ export function reassignFacesById({ id, faceDto }: { id: string; faceDto: FaceDto; @@ -2394,6 +2547,9 @@ export function reassignFacesById({ id, faceDto }: { body: faceDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.read` permission. + */ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2402,6 +2558,9 @@ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function createJob({ jobCreateDto }: { jobCreateDto: JobCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2411,6 +2570,9 @@ export function createJob({ jobCreateDto }: { body: jobCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function sendJobCommand({ id, jobCommandDto }: { id: JobName; jobCommandDto: JobCommandDto; @@ -2424,6 +2586,9 @@ export function sendJobCommand({ id, jobCommandDto }: { body: jobCommandDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2432,6 +2597,9 @@ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.create` permission. + */ export function createLibrary({ createLibraryDto }: { createLibraryDto: CreateLibraryDto; }, opts?: Oazapfts.RequestOpts) { @@ -2444,6 +2612,9 @@ export function createLibrary({ createLibraryDto }: { body: createLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.delete` permission. + */ export function deleteLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2452,6 +2623,9 @@ export function deleteLibrary({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2462,6 +2636,9 @@ export function getLibrary({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function updateLibrary({ id, updateLibraryDto }: { id: string; updateLibraryDto: UpdateLibraryDto; @@ -2475,6 +2652,9 @@ export function updateLibrary({ id, updateLibraryDto }: { body: updateLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function scanLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2483,6 +2663,9 @@ export function scanLibrary({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `library.statistics` permission. + */ export function getLibraryStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2506,11 +2689,11 @@ export function validate({ id, validateLibraryDto }: { body: validateLibraryDto }))); } -export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners, withSharedAlbums }: { - fileCreatedAfter?: string; - fileCreatedBefore?: string; +export function getMapMarkers({ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, withPartners, withSharedAlbums }: { isArchived?: boolean; isFavorite?: boolean; + fileCreatedAfter?: string; + fileCreatedBefore?: string; withPartners?: boolean; withSharedAlbums?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -2518,10 +2701,10 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, status: 200; data: MapMarkerResponseDto[]; }>(`/map/markers${QS.query(QS.explode({ - fileCreatedAfter, - fileCreatedBefore, isArchived, isFavorite, + fileCreatedAfter, + fileCreatedBefore, withPartners, withSharedAlbums }))}`, { @@ -2542,6 +2725,9 @@ export function reverseGeocode({ lat, lon }: { ...opts })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function searchMemories({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2560,6 +2746,9 @@ export function searchMemories({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.create` permission. + */ export function createMemory({ memoryCreateDto }: { memoryCreateDto: MemoryCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2572,6 +2761,9 @@ export function createMemory({ memoryCreateDto }: { body: memoryCreateDto }))); } +/** + * This endpoint requires the `memory.statistics` permission. + */ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2590,6 +2782,9 @@ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.delete` permission. + */ export function deleteMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2598,6 +2793,9 @@ export function deleteMemory({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function getMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2608,6 +2806,9 @@ export function getMemory({ id }: { ...opts })); } +/** + * This endpoint requires the `memory.update` permission. + */ export function updateMemory({ id, memoryUpdateDto }: { id: string; memoryUpdateDto: MemoryUpdateDto; @@ -2621,6 +2822,9 @@ export function updateMemory({ id, memoryUpdateDto }: { body: memoryUpdateDto }))); } +/** + * This endpoint requires the `memoryAsset.delete` permission. + */ export function removeMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2634,6 +2838,9 @@ export function removeMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `memoryAsset.create` permission. + */ export function addMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2647,6 +2854,9 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotifications({ notificationDeleteAllDto }: { notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2656,6 +2866,9 @@ export function deleteNotifications({ notificationDeleteAllDto }: { body: notificationDeleteAllDto }))); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotifications({ id, level, $type, unread }: { id?: string; level?: NotificationLevel; @@ -2674,6 +2887,9 @@ export function getNotifications({ id, level, $type, unread }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotifications({ notificationUpdateAllDto }: { notificationUpdateAllDto: NotificationUpdateAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2683,6 +2899,9 @@ export function updateNotifications({ notificationUpdateAllDto }: { body: notificationUpdateAllDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2691,6 +2910,9 @@ export function deleteNotification({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2701,6 +2923,9 @@ export function getNotification({ id }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotification({ id, notificationUpdateDto }: { id: string; notificationUpdateDto: NotificationUpdateDto; @@ -2764,6 +2989,9 @@ export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `partner.read` permission. + */ export function getPartners({ direction }: { direction: PartnerDirection; }, opts?: Oazapfts.RequestOpts) { @@ -2776,6 +3004,9 @@ export function getPartners({ direction }: { ...opts })); } +/** + * This endpoint requires the `partner.delete` permission. + */ export function removePartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2784,6 +3015,9 @@ export function removePartner({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `partner.create` permission. + */ export function createPartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2795,6 +3029,9 @@ export function createPartner({ id }: { method: "POST" })); } +/** + * This endpoint requires the `partner.update` permission. + */ export function updatePartner({ id, updatePartnerDto }: { id: string; updatePartnerDto: UpdatePartnerDto; @@ -2808,6 +3045,9 @@ export function updatePartner({ id, updatePartnerDto }: { body: updatePartnerDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePeople({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2817,6 +3057,9 @@ export function deletePeople({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function getAllPeople({ closestAssetId, closestPersonId, page, size, withHidden }: { closestAssetId?: string; closestPersonId?: string; @@ -2837,6 +3080,9 @@ export function getAllPeople({ closestAssetId, closestPersonId, page, size, with ...opts })); } +/** + * This endpoint requires the `person.create` permission. + */ export function createPerson({ personCreateDto }: { personCreateDto: PersonCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2849,6 +3095,9 @@ export function createPerson({ personCreateDto }: { body: personCreateDto }))); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePeople({ peopleUpdateDto }: { peopleUpdateDto: PeopleUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2861,6 +3110,9 @@ export function updatePeople({ peopleUpdateDto }: { body: peopleUpdateDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2869,6 +3121,9 @@ export function deletePerson({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2879,6 +3134,9 @@ export function getPerson({ id }: { ...opts })); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePerson({ id, personUpdateDto }: { id: string; personUpdateDto: PersonUpdateDto; @@ -2892,6 +3150,9 @@ export function updatePerson({ id, personUpdateDto }: { body: personUpdateDto }))); } +/** + * This endpoint requires the `person.merge` permission. + */ export function mergePerson({ id, mergePersonDto }: { id: string; mergePersonDto: MergePersonDto; @@ -2905,6 +3166,9 @@ export function mergePerson({ id, mergePersonDto }: { body: mergePersonDto }))); } +/** + * This endpoint requires the `person.reassign` permission. + */ export function reassignFaces({ id, assetFaceUpdateDto }: { id: string; assetFaceUpdateDto: AssetFaceUpdateDto; @@ -2918,6 +3182,9 @@ export function reassignFaces({ id, assetFaceUpdateDto }: { body: assetFaceUpdateDto }))); } +/** + * This endpoint requires the `person.statistics` permission. + */ export function getPersonStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2928,6 +3195,9 @@ export function getPersonStatistics({ id }: { ...opts })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPersonThumbnail({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2938,6 +3208,9 @@ export function getPersonThumbnail({ id }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2946,6 +3219,9 @@ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getExploreData(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2954,6 +3230,9 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { albumIds?: string[]; city?: string | null; @@ -3027,6 +3306,9 @@ export function searchLargeAssets({ albumIds, city, country, createdAfter, creat method: "POST" })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3039,6 +3321,9 @@ export function searchAssets({ metadataSearchDto }: { body: metadataSearchDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function searchPerson({ name, withHidden }: { name: string; withHidden?: boolean; @@ -3053,6 +3338,9 @@ export function searchPerson({ name, withHidden }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchPlaces({ name }: { name: string; }, opts?: Oazapfts.RequestOpts) { @@ -3065,6 +3353,9 @@ export function searchPlaces({ name }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchRandom({ randomSearchDto }: { randomSearchDto: RandomSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3077,6 +3368,9 @@ export function searchRandom({ randomSearchDto }: { body: randomSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchSmart({ smartSearchDto }: { smartSearchDto: SmartSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3089,6 +3383,9 @@ export function searchSmart({ smartSearchDto }: { body: smartSearchDto }))); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function searchAssetStatistics({ statisticsSearchDto }: { statisticsSearchDto: StatisticsSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3101,6 +3398,9 @@ export function searchAssetStatistics({ statisticsSearchDto }: { body: statisticsSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getSearchSuggestions({ country, includeNull, make, model, state, $type }: { country?: string; includeNull?: boolean; @@ -3123,6 +3423,9 @@ export function getSearchSuggestions({ country, includeNull, make, model, state, ...opts })); } +/** + * This endpoint requires the `server.about` permission. + */ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3131,6 +3434,9 @@ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.apkLinks` permission. + */ export function getApkLinks(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3155,12 +3461,18 @@ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + */ export function deleteServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/server/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + */ export function getServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3171,6 +3483,9 @@ export function getServerLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + */ export function setServerLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3199,6 +3514,9 @@ export function pingServer(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `server.statistics` permission. + */ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3207,6 +3525,9 @@ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.storage` permission. + */ export function getStorage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3247,12 +3568,18 @@ export function getVersionHistory(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteAllSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/sessions", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `session.read` permission. + */ export function getSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3261,6 +3588,9 @@ export function getSessions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.create` permission. + */ export function createSession({ sessionCreateDto }: { sessionCreateDto: SessionCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3273,6 +3603,9 @@ export function createSession({ sessionCreateDto }: { body: sessionCreateDto }))); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3281,6 +3614,9 @@ export function deleteSession({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `session.update` permission. + */ export function updateSession({ id, sessionUpdateDto }: { id: string; sessionUpdateDto: SessionUpdateDto; @@ -3294,6 +3630,9 @@ export function updateSession({ id, sessionUpdateDto }: { body: sessionUpdateDto }))); } +/** + * This endpoint requires the `session.lock` permission. + */ export function lockSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3302,6 +3641,9 @@ export function lockSession({ id }: { method: "POST" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getAllSharedLinks({ albumId }: { albumId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3314,6 +3656,9 @@ export function getAllSharedLinks({ albumId }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.create` permission. + */ export function createSharedLink({ sharedLinkCreateDto }: { sharedLinkCreateDto: SharedLinkCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3326,24 +3671,27 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, slug, token }: { - key?: string; +export function getMySharedLink({ password, token, key, slug }: { password?: string; - slug?: string; token?: string; + key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: SharedLinkResponseDto; }>(`/shared-links/me${QS.query(QS.explode({ - key, password, - slug, - token + token, + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `sharedLink.delete` permission. + */ export function removeSharedLink({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3352,6 +3700,9 @@ export function removeSharedLink({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getSharedLinkById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3362,6 +3713,9 @@ export function getSharedLinkById({ id }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.update` permission. + */ export function updateSharedLink({ id, sharedLinkEditDto }: { id: string; sharedLinkEditDto: SharedLinkEditDto; @@ -3411,6 +3765,9 @@ export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { body: assetIdsDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStacks({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3420,6 +3777,9 @@ export function deleteStacks({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `stack.read` permission. + */ export function searchStacks({ primaryAssetId }: { primaryAssetId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3432,6 +3792,9 @@ export function searchStacks({ primaryAssetId }: { ...opts })); } +/** + * This endpoint requires the `stack.create` permission. + */ export function createStack({ stackCreateDto }: { stackCreateDto: StackCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3444,6 +3807,9 @@ export function createStack({ stackCreateDto }: { body: stackCreateDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3452,6 +3818,9 @@ export function deleteStack({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `stack.read` permission. + */ export function getStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3462,6 +3831,9 @@ export function getStack({ id }: { ...opts })); } +/** + * This endpoint requires the `stack.update` permission. + */ export function updateStack({ id, stackUpdateDto }: { id: string; stackUpdateDto: StackUpdateDto; @@ -3475,6 +3847,9 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +/** + * This endpoint requires the `stack.update` permission. + */ export function removeAssetFromStack({ assetId, id }: { assetId: string; id: string; @@ -3484,6 +3859,9 @@ export function removeAssetFromStack({ assetId, id }: { method: "DELETE" })); } +/** + * This endpoint requires the `syncCheckpoint.delete` permission. + */ export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -3493,6 +3871,9 @@ export function deleteSyncAck({ syncAckDeleteDto }: { body: syncAckDeleteDto }))); } +/** + * This endpoint requires the `syncCheckpoint.read` permission. + */ export function getSyncAck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3501,6 +3882,9 @@ export function getSyncAck(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `syncCheckpoint.update` permission. + */ export function sendSyncAck({ syncAckSetDto }: { syncAckSetDto: SyncAckSetDto; }, opts?: Oazapfts.RequestOpts) { @@ -3534,6 +3918,9 @@ export function getFullSyncForUser({ assetFullSyncDto }: { body: assetFullSyncDto }))); } +/** + * This endpoint requires the `sync.stream` permission. + */ export function getSyncStream({ syncStreamDto }: { syncStreamDto: SyncStreamDto; }, opts?: Oazapfts.RequestOpts) { @@ -3543,6 +3930,9 @@ export function getSyncStream({ syncStreamDto }: { body: syncStreamDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3551,6 +3941,9 @@ export function getConfig(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + */ export function updateConfig({ systemConfigDto }: { systemConfigDto: SystemConfigDto; }, opts?: Oazapfts.RequestOpts) { @@ -3563,6 +3956,9 @@ export function updateConfig({ systemConfigDto }: { body: systemConfigDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3571,6 +3967,9 @@ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3579,6 +3978,9 @@ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3587,6 +3989,9 @@ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + */ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { adminOnboardingUpdateDto: AdminOnboardingUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3596,6 +4001,9 @@ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { body: adminOnboardingUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3604,6 +4012,9 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3612,6 +4023,9 @@ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getAllTags(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3620,6 +4034,9 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.create` permission. + */ export function createTag({ tagCreateDto }: { tagCreateDto: TagCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3632,6 +4049,9 @@ export function createTag({ tagCreateDto }: { body: tagCreateDto }))); } +/** + * This endpoint requires the `tag.create` permission. + */ export function upsertTags({ tagUpsertDto }: { tagUpsertDto: TagUpsertDto; }, opts?: Oazapfts.RequestOpts) { @@ -3644,6 +4064,9 @@ export function upsertTags({ tagUpsertDto }: { body: tagUpsertDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function bulkTagAssets({ tagBulkAssetsDto }: { tagBulkAssetsDto: TagBulkAssetsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3656,6 +4079,9 @@ export function bulkTagAssets({ tagBulkAssetsDto }: { body: tagBulkAssetsDto }))); } +/** + * This endpoint requires the `tag.delete` permission. + */ export function deleteTag({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3664,6 +4090,9 @@ export function deleteTag({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getTagById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3674,6 +4103,9 @@ export function getTagById({ id }: { ...opts })); } +/** + * This endpoint requires the `tag.update` permission. + */ export function updateTag({ id, tagUpdateDto }: { id: string; tagUpdateDto: TagUpdateDto; @@ -3687,6 +4119,9 @@ export function updateTag({ id, tagUpdateDto }: { body: tagUpdateDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function untagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3700,6 +4135,9 @@ export function untagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function tagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3713,6 +4151,9 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; @@ -3749,6 +4190,9 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; @@ -3783,6 +4227,9 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per ...opts })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function emptyTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3792,6 +4239,9 @@ export function emptyTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3801,6 +4251,9 @@ export function restoreTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreAssets({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3813,6 +4266,9 @@ export function restoreAssets({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function searchUsers(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3821,6 +4277,9 @@ export function searchUsers(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.read` permission. + */ export function getMyUser(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3829,6 +4288,9 @@ export function getMyUser(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.update` permission. + */ export function updateMyUser({ userUpdateMeDto }: { userUpdateMeDto: UserUpdateMeDto; }, opts?: Oazapfts.RequestOpts) { @@ -3841,12 +4303,18 @@ export function updateMyUser({ userUpdateMeDto }: { body: userUpdateMeDto }))); } +/** + * This endpoint requires the `userLicense.delete` permission. + */ export function deleteUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userLicense.read` permission. + */ export function getUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3855,6 +4323,9 @@ export function getUserLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userLicense.update` permission. + */ export function setUserLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3867,12 +4338,18 @@ export function setUserLicense({ licenseKeyDto }: { body: licenseKeyDto }))); } +/** + * This endpoint requires the `userOnboarding.delete` permission. + */ export function deleteUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/onboarding", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userOnboarding.read` permission. + */ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3881,6 +4358,9 @@ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userOnboarding.update` permission. + */ export function setUserOnboarding({ onboardingDto }: { onboardingDto: OnboardingDto; }, opts?: Oazapfts.RequestOpts) { @@ -3893,6 +4373,9 @@ export function setUserOnboarding({ onboardingDto }: { body: onboardingDto }))); } +/** + * This endpoint requires the `userPreference.read` permission. + */ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3901,6 +4384,9 @@ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userPreference.update` permission. + */ export function updateMyPreferences({ userPreferencesUpdateDto }: { userPreferencesUpdateDto: UserPreferencesUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3913,12 +4399,18 @@ export function updateMyPreferences({ userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint requires the `userProfileImage.delete` permission. + */ export function deleteProfileImage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/profile-image", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userProfileImage.update` permission. + */ export function createProfileImage({ createProfileImageDto }: { createProfileImageDto: CreateProfileImageDto; }, opts?: Oazapfts.RequestOpts) { @@ -3931,6 +4423,9 @@ export function createProfileImage({ createProfileImageDto }: { body: createProfileImageDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function getUser({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3941,6 +4436,9 @@ export function getUser({ id }: { ...opts })); } +/** + * This endpoint requires the `userProfileImage.read` permission. + */ export function getProfileImage({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/enum.ts b/server/src/enum.ts index 666b9fc505..93d271f19c 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -413,6 +413,11 @@ export enum LogLevel { Fatal = 'fatal', } +export enum ApiCustomExtension { + Permission = 'x-immich-permission', + AdminOnly = 'x-immich-admin-only', +} + export enum MetadataKey { AuthRoute = 'auth_route', AdminRoute = 'admin_route', diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 38ff1c373f..80d7a37435 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -10,7 +10,7 @@ import { Reflector } from '@nestjs/core'; import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; +import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UAParser } from 'ua-parser-js'; @@ -19,16 +19,20 @@ type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute); -export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => { +export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => { const decorators: MethodDecorator[] = [ ApiBearerAuth(), ApiCookieAuth(), ApiSecurity(MetadataKey.ApiKeySecurity), - SetMetadata(MetadataKey.AuthRoute, options || {}), + SetMetadata(MetadataKey.AuthRoute, options), ]; + if ((options as AdminRoute).admin) { + decorators.push(ApiExtension(ApiCustomExtension.AdminOnly, true)); + } + if (options?.permission) { - decorators.push(ApiExtension('x-immich-permission', options.permission)); + decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission ?? Permission.All)); } if ((options as SharedLinkRoute)?.sharedLink) { diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 3acb72b663..a32632b52d 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -6,7 +6,11 @@ import { SwaggerDocumentOptions, SwaggerModule, } from '@nestjs/swagger'; -import { ReferenceObject, SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; +import { + OperationObject, + ReferenceObject, + SchemaObject, +} from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import _ from 'lodash'; import { writeFileSync } from 'node:fs'; import path from 'node:path'; @@ -15,7 +19,7 @@ import parse from 'picomatch/lib/parse'; import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, serverVersion } from 'src/constants'; import { extraSyncModels } from 'src/dtos/sync.dto'; -import { ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; +import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export class ImmichStartupError extends Error {} @@ -198,7 +202,12 @@ const patchOpenAPI = (document: OpenAPIObject) => { trace: path.trace, }; - for (const operation of Object.values(operations)) { + for (const operation of Object.values(operations) as Array< + OperationObject & { + [ApiCustomExtension.AdminOnly]?: boolean; + [ApiCustomExtension.Permission]?: string; + } + >) { if (!operation) { continue; } @@ -211,12 +220,21 @@ const patchOpenAPI = (document: OpenAPIObject) => { // console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`); } - if (operation.description === '') { - delete operation.description; - } + const adminOnly = operation[ApiCustomExtension.AdminOnly] ?? false; + const permission = operation[ApiCustomExtension.Permission]; + if (permission) { + let description = (operation.description || '').trim(); + if (description && !description.endsWith('.')) { + description += '. '; + } - if (operation.parameters) { - operation.parameters = _.orderBy(operation.parameters, 'name'); + operation.description = + description + + `This endpoint ${adminOnly ? 'is an admin-only route, and ' : ''}requires the \`${permission}\` permission.`; + + if (operation.parameters) { + operation.parameters = _.orderBy(operation.parameters, 'name'); + } } } } From 47a025f39f4afcdc1ea0592e2f4f2bff4faf2485 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Wed, 30 Jul 2025 18:30:21 +0200 Subject: [PATCH 146/169] chore: also run docs build on oapi changes (#20440) --- .github/workflows/docs-build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 93b6c8ad04..36a9a96711 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -18,7 +18,7 @@ jobs: permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.found_paths.outputs.open-api == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -32,6 +32,8 @@ jobs: - 'docs/**' workflow: - '.github/workflows/docs-build.yml' + open-api: + - 'open-api/immich-openapi-specs.json' - name: Check if we should force jobs to run id: should_force run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT" From 10e9c278eefd78ee71eed668145c71fadee1e39f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 30 Jul 2025 11:43:20 -0500 Subject: [PATCH 147/169] feat: network requirement option for upload (#20302) * wifi toggle * feat: network requirement option for upload * chore: put back holding queue previous config numbers * options * backup option page * pr feedback --- i18n/en.json | 5 ++ mobile/lib/domain/models/store.model.dart | 4 +- mobile/lib/main.dart | 3 +- .../lib/pages/backup/drift_backup.page.dart | 9 ++ .../backup/drift_backup_options.page.dart | 68 +++++++++++++++ mobile/lib/pages/common/settings.page.dart | 7 +- .../backup/drift_backup.provider.dart | 8 +- mobile/lib/routing/router.dart | 4 +- mobile/lib/routing/router.gr.dart | 16 ++++ mobile/lib/services/app_settings.service.dart | 4 +- mobile/lib/services/upload.service.dart | 24 +++++- .../drift_backup_settings.dart | 82 +++++++++++++++++++ 12 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 mobile/lib/pages/backup/drift_backup_options.page.dart create mode 100644 mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart diff --git a/i18n/en.json b/i18n/en.json index c445110996..94f6920745 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -580,8 +580,10 @@ "backup_manual_in_progress": "Upload already in progress. Try after sometime", "backup_manual_success": "Success", "backup_manual_title": "Upload status", + "backup_options": "Backup Options", "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_settings_subtitle": "Manage upload settings", "backward": "Backward", "beta_sync": "Beta Sync Status", "beta_sync_subtitle": "Manage the new sync system", @@ -1312,6 +1314,9 @@ "my_albums": "My albums", "name": "Name", "name_or_nickname": "Name or nickname", + "network_requirement_photos_upload": "Use cellular data to backup photos", + "network_requirement_videos_upload": "Use cellular data to backup videos", + "network_requirements_updated": "Network requirements changed, resetting backup queue", "networking_settings": "Networking", "networking_subtitle": "Manage the server endpoint settings", "never": "Never", diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 305b3f3387..e4e316b814 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -71,7 +71,9 @@ enum StoreKey { photoManagerCustomFilter._(1000), betaPromptShown._(1001), betaTimeline._(1002), - enableBackup._(1003); + enableBackup._(1003), + useWifiForUploadVideos._(1004), + useWifiForUploadPhotos._(1005); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 63220295ff..d1f415a304 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -91,10 +91,9 @@ Future initApp() async { initializeTimeZones(); // Initialize the file downloader - await FileDownloader().configure( // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 - globalConfig: (Config.holdingQueue, (1000, 1000, 1000)), + globalConfig: (Config.holdingQueue, (6, 6, 3)), ); await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 6d31c75946..70e8190014 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -65,6 +65,15 @@ class _DriftBackupPageState extends ConsumerState { splashRadius: 24, icon: const Icon(Icons.arrow_back_ios_rounded), ), + actions: [ + IconButton( + onPressed: () { + context.pushRoute(const DriftBackupOptionsRoute()); + }, + icon: const Icon(Icons.settings_outlined), + tooltip: "backup_options".t(context: context), + ), + ], ), body: Stack( children: [ diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart new file mode 100644 index 0000000000..92f911ae1e --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -0,0 +1,68 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; + +@RoutePage() +class DriftBackupOptionsPage extends ConsumerWidget { + const DriftBackupOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasPopped = false; + final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + + final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + + if (currentWifiReqForVideos == previousWifiReqForVideos && + currentWifiReqForPhotos == previousWifiReqForPhotos) { + return; + } + + if (didPop && !hasPopped) { + hasPopped = true; + + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + if (!isBackupEnabled) { + return; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("network_requirements_updated".t(context: context)), + duration: const Duration(seconds: 4), + ), + ); + + final backupNotifier = ref.read(driftBackupProvider.notifier); + backupNotifier.cancel().then((_) { + backupNotifier.startBackup(currentUser.id); + }); + } + }, + child: Scaffold( + appBar: AppBar(title: Text("backup_options".t(context: context))), + body: const DriftBackupSettings(), + ), + ); + } +} diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index d7ecb7e582..7bc8cd2b3a 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -9,6 +10,7 @@ import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; @@ -21,7 +23,7 @@ enum SettingSection { beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), - backup('backup', Icons.cloud_upload_outlined, "backup_setting_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), languages('language', Icons.language, "setting_languages_subtitle"), networking('networking_settings', Icons.wifi, "networking_subtitle"), notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), @@ -36,7 +38,8 @@ enum SettingSection { SettingSection.beta => const _BetaLandscapeToggle(), SettingSection.advanced => const AdvancedSettings(), SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), + SettingSection.backup => + Store.tryGet(StoreKey.betaTimeline) ?? false ? const DriftBackupSettings() : const BackupSettings(), SettingSection.languages => const LanguageSettings(), SettingSection.networking => const NetworkingSettings(), SettingSection.notifications => const NotificationSetting(), diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index aca10e60a7..39949fd526 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -187,12 +187,12 @@ class DriftBackupState { } } -final driftBackupProvider = StateNotifierProvider((ref) { - return ExpBackupNotifier(ref.watch(uploadServiceProvider)); +final driftBackupProvider = StateNotifierProvider((ref) { + return DriftBackupNotifier(ref.watch(uploadServiceProvider)); }); -class ExpBackupNotifier extends StateNotifier { - ExpBackupNotifier(this._uploadService) +class DriftBackupNotifier extends StateNotifier { + DriftBackupNotifier(this._uploadService) : super( const DriftBackupState( totalCount: 0, diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 54d4d080d4..4fe1673893 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -28,6 +28,7 @@ import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart'; import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; @@ -322,13 +323,12 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), + AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 63e8c6ecfe..e8f0dd8b1f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -764,6 +764,22 @@ class DriftBackupAlbumSelectionRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftBackupOptionsPage] +class DriftBackupOptionsRoute extends PageRouteInfo { + const DriftBackupOptionsRoute({List? children}) + : super(DriftBackupOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftBackupOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupOptionsPage(); + }, + ); +} + /// generated route for /// [DriftBackupPage] class DriftBackupRoute extends PageRouteInfo { diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index e705912aa5..8a4b0c6719 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -47,7 +47,9 @@ enum AppSettingsEnum { autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, false), - enableBackup(StoreKey.enableBackup, null, false); + enableBackup(StoreKey.enableBackup, null, false), + useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), + useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index c41d2b2e5f..dba3817b2c 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -12,11 +12,13 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:path/path.dart' as p; final uploadServiceProvider = Provider((ref) { @@ -25,6 +27,7 @@ final uploadServiceProvider = Provider((ref) { ref.watch(backupRepositoryProvider), ref.watch(storageRepositoryProvider), ref.watch(localAssetRepository), + ref.watch(appSettingsServiceProvider), ); ref.onDispose(service.dispose); @@ -32,7 +35,13 @@ final uploadServiceProvider = Provider((ref) { }); class UploadService { - UploadService(this._uploadRepository, this._backupRepository, this._storageRepository, this._localAssetRepository) { + UploadService( + this._uploadRepository, + this._backupRepository, + this._storageRepository, + this._localAssetRepository, + this._appSettingsService, + ) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -41,6 +50,7 @@ class UploadService { final DriftBackupRepository _backupRepository; final StorageRepository _storageRepository; final DriftLocalAssetRepository _localAssetRepository; + final AppSettingsService _appSettingsService; final StreamController _taskStatusController = StreamController.broadcast(); final StreamController _taskProgressController = StreamController.broadcast(); @@ -240,6 +250,14 @@ class UploadService { livePhotoVideoId: '', ).toJson(); + bool requiresWiFi = true; + + if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) { + requiresWiFi = false; + } else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) { + requiresWiFi = false; + } + return buildUploadTask( file, originalFileName: originalFileName, @@ -248,6 +266,7 @@ class UploadService { group: group, priority: priority, isFavorite: asset.isFavorite, + requiresWiFi: requiresWiFi, ); } @@ -284,12 +303,12 @@ class UploadService { String? metadata, int? priority, bool? isFavorite, + bool requiresWiFi = true, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); final deviceId = Store.get(StoreKey.deviceId); - final (baseDirectory, directory, filename) = await Task.split(filePath: file.path); final stats = await file.stat(); final fileCreatedAt = stats.changed; @@ -318,6 +337,7 @@ class UploadService { fileField: 'assetData', metaData: metadata ?? '', group: group, + requiresWiFi: requiresWiFi, priority: priority ?? 5, updates: Updates.statusAndProgress, retries: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart new file mode 100644 index 0000000000..553eb939c2 --- /dev/null +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; + +class DriftBackupSettings extends StatelessWidget { + const DriftBackupSettings({super.key}); + + @override + Widget build(BuildContext context) { + return const SettingsSubPageScaffold(settings: [_UseWifiForUploadVideosButton(), _UseWifiForUploadPhotosButton()]); + } +} + +class _UseWifiForUploadVideosButton extends ConsumerWidget { + const _UseWifiForUploadVideosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadVideos); + + return ListTile( + title: Text( + "videos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_videos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadVideos, newValue); + }, + ); + }, + ), + ); + } +} + +class _UseWifiForUploadPhotosButton extends ConsumerWidget { + const _UseWifiForUploadPhotosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadPhotos); + + return ListTile( + title: Text( + "photos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_photos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadPhotos, newValue); + }, + ); + }, + ), + ); + } +} From c278b7ad17ebff19bdd8019caee380ad66077dc6 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 30 Jul 2025 19:16:47 +0200 Subject: [PATCH 148/169] chore(web): update translations (#20105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: AbuKareem Tuffaha Co-authored-by: Adam Uchmanowicz Co-authored-by: AgentTricky Co-authored-by: Alexandre Garnier Co-authored-by: Alvin Co-authored-by: Andreas Johansen Co-authored-by: Bartłomiej <20731216+Jarsey45@users.noreply.github.com> Co-authored-by: Bartłomiej Co-authored-by: Benjamin Graf Co-authored-by: CanbiZ Co-authored-by: Cezar Olteanu Co-authored-by: Christoph Auer Co-authored-by: Dag Stuan Co-authored-by: Davide Ciaccia Co-authored-by: Davide Vegliante Co-authored-by: Dennis Premoli Co-authored-by: DevServs Co-authored-by: Dmitry Banny Co-authored-by: Felipe Silva Co-authored-by: Fjuro Co-authored-by: Florian Ostertag Co-authored-by: Hurricane-32 Co-authored-by: Indrek Haav Co-authored-by: Javier Villanueva García Co-authored-by: John Molkavitch Co-authored-by: Jordy H Co-authored-by: Jozef Gaal Co-authored-by: Kasper Honoré Co-authored-by: Lauritz Tieste Co-authored-by: Leo Bottaro Co-authored-by: MaBeniu Co-authored-by: Mateo Varela Co-authored-by: Matjaž T Co-authored-by: Mikkel Dupont Olesen Co-authored-by: Musab Ustun Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Nick Huang Co-authored-by: Nico Kaiser Co-authored-by: Nicolò Co-authored-by: Nikolina Babok Co-authored-by: Pavel Miniutka Co-authored-by: Petri Hämäläinen Co-authored-by: Phantom0174 Co-authored-by: R J Co-authored-by: Raman Venmarathoor Co-authored-by: Santiago Co-authored-by: Saschl Co-authored-by: Sergey Katsubo Co-authored-by: Shawn Co-authored-by: Sylvain Pichon Co-authored-by: Tajbir Prottoy Co-authored-by: Tanishq Sindhu Co-authored-by: Tijs-B Co-authored-by: Tony Ronaldo Matute Co-authored-by: User 123456789 Co-authored-by: Vegard Fladby Co-authored-by: Xo Co-authored-by: Zvonimir Co-authored-by: adri1m64 Co-authored-by: chamdim Co-authored-by: eav5jhl0 Co-authored-by: grgergo Co-authored-by: johnwoo_nl Co-authored-by: manosrh Co-authored-by: maxius65 Co-authored-by: neketos851 Co-authored-by: over adm <1ron3gg@gmail.com> Co-authored-by: pyccl Co-authored-by: syyhs668 <812741539@qq.com> Co-authored-by: thehijacker Co-authored-by: traptegies Co-authored-by: tsloms Co-authored-by: waclaw66 --- i18n/ar.json | 26 +++- i18n/be.json | 91 ++++++++++++- i18n/bg.json | 3 +- i18n/bn.json | 2 +- i18n/ca.json | 3 +- i18n/cs.json | 37 +++++- i18n/da.json | 18 ++- i18n/de.json | 49 +++++-- i18n/el.json | 87 +++++++++++- i18n/es.json | 62 +++++++-- i18n/et.json | 35 ++++- i18n/fa.json | 2 - i18n/fi.json | 4 +- i18n/fr.json | 40 ++++-- i18n/gl.json | 3 +- i18n/he.json | 91 +++++++++++-- i18n/hi.json | 245 ++++++++++++++++++++++++++++++++-- i18n/hr.json | 117 ++++++++++++++-- i18n/hu.json | 39 +++++- i18n/hy.json | 1 - i18n/id.json | 3 +- i18n/it.json | 58 +++++++- i18n/ja.json | 3 +- i18n/ko.json | 3 +- i18n/lt.json | 28 +++- i18n/lv.json | 26 +++- i18n/mk.json | 1 - i18n/ml.json | 63 ++++++++- i18n/nb_NO.json | 103 ++++++++------ i18n/nl.json | 83 +++++++----- i18n/pl.json | 54 +++++++- i18n/pt.json | 55 ++++++-- i18n/pt_BR.json | 77 ++++++++++- i18n/ro.json | 287 ++++++++++++++++++++++++++++++++++++++-- i18n/ru.json | 43 ++++-- i18n/sk.json | 97 ++++++++------ i18n/sl.json | 31 ++++- i18n/sr_Cyrl.json | 3 +- i18n/sr_Latn.json | 3 +- i18n/sv.json | 7 +- i18n/ta.json | 1 - i18n/te.json | 1 - i18n/th.json | 3 +- i18n/tr.json | 91 ++++++++++++- i18n/uk.json | 13 +- i18n/vi.json | 3 +- i18n/zh_Hant.json | 42 +++++- i18n/zh_SIMPLIFIED.json | 31 +++-- 48 files changed, 1850 insertions(+), 318 deletions(-) diff --git a/i18n/ar.json b/i18n/ar.json index 6042c8f82f..13e785668d 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -393,6 +393,7 @@ "album_cover_updated": "تم تحديث غلاف الألبوم", "album_delete_confirmation": "هل أنت متأكد أنك تريد حذف الألبوم {album}؟", "album_delete_confirmation_description": "إذا تمت مشاركة هذا الألبوم، فلن يتمكن المستخدمون الآخرون من الوصول إليه بعد الآن.", + "album_deleted": "تم حذف الالبوم", "album_info_card_backup_album_excluded": "مستبعد", "album_info_card_backup_album_included": "متضمنة", "album_info_updated": "تم تحديث معلومات الألبوم", @@ -402,6 +403,7 @@ "album_options": "إعدادات الألبوم", "album_remove_user": "هل ترغب في إزالة المستخدم؟", "album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟", + "album_search_not_found": "لم يتم ايجاد البوم مطابق لبحثك", "album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.", "album_updated": "تم تحديث الألبوم", "album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة", @@ -421,6 +423,7 @@ "albums_default_sort_order": "ترتيب الألبوم الافتراضي", "albums_default_sort_order_description": "ترتيب فرز الأصول الأولي عند إنشاء ألبومات جديدة.", "albums_feature_description": "مجموعة من الأصول التي يمكن مشاركتها مع مستخدمين آخرين.", + "albums_on_device_count": "عدد الالبومات على الجهاز ({count})", "all": "الكل", "all_albums": "جميع الألبومات", "all_people": "جميع الأشخاص", @@ -435,7 +438,7 @@ "api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.", "api_key_empty": "يجب ألا يكون اسم مفتاح API فارغًا", "api_keys": "مفاتيح API", - "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد الخروج", + "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد تسجيل الخروج؟", "app_bar_signout_dialog_ok": "نعم", "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", @@ -504,6 +507,7 @@ "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", "background_location_permission": "اذن الوصول للموقع في الخلفية", "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "backup": "دعم", "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", "backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.", @@ -567,6 +571,8 @@ "backup_options_page_title": "خيارات النسخ الاحتياطي", "backup_setting_subtitle": "ادارة اعدادات التحميل في الخلفية والمقدمة", "backward": "الى الوراء", + "beta_sync": "حالة المزامنة التجريبية", + "beta_sync_subtitle": "ادارة نظام المزامنة الجديد", "biometric_auth_enabled": "المصادقة البايومترية مفعله", "biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية", "biometric_no_options": "لا توجد خيارات بايومترية متوفرة", @@ -584,7 +590,7 @@ "cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت", "cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.", "cache_settings_duplicated_assets_clear_button": "واضح", - "cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق", + "cache_settings_duplicated_assets_subtitle": "الصور والفيديوهات اللتي تم تجاهلها في التطبيق", "cache_settings_duplicated_assets_title": "الاصول المكررة ({count})", "cache_settings_statistics_album": "مكتبه الصور المصغره", "cache_settings_statistics_full": "صور كاملة", @@ -601,6 +607,7 @@ "cancel": "إلغاء", "cancel_search": "الغاء البحث", "canceled": "تم الالغاء", + "canceling": "جارِ الالغاء", "cannot_merge_people": "لا يمكن دمج الأشخاص", "cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!", "cannot_update_the_description": "لا يمكن تحديث الوصف", @@ -616,7 +623,7 @@ "change_password": "تغيير كلمة المرور", "change_password_description": "هذه إما هي المرة الأولى التي تقوم فيها بتسجيل الدخول إلى النظام أو أنه تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_confirm_password": "تأكيد كلمة المرور", - "change_password_form_description": "مرحبًا ،هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك.الرجاء إدخال كلمة المرور الجديدة أدناه", + "change_password_form_description": "مرحبًا {name}،\n\nاما ان تكون هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_new_password": "كلمة المرور الجديدة", "change_password_form_password_mismatch": "كلمة المرور غير مطابقة", "change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة", @@ -733,7 +740,8 @@ "default_locale": "اللغة الافتراضية", "default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك", "delete": "حذف", - "delete_action_prompt": "{count} حذف بشكل نهائي", + "delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز", + "delete_action_prompt": "تم حذف {count}", "delete_album": "حذف الألبوم", "delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", "delete_dialog_alert": "هذه العناصر سيتم حذفها بشكل دائم من Immich و من جهازك", @@ -747,9 +755,12 @@ "delete_key": "حذف المفتاح", "delete_library": "حذف المكتبة", "delete_link": "حذف الرابط", + "delete_local_action_prompt": "تم حذف {count} من الجهاز", "delete_local_dialog_ok_backed_up_only": "حذف النسخة الاحتياطية فقط", "delete_local_dialog_ok_force": "احذف على أي حال", "delete_others": "حذف الأخرى", + "delete_permanently": "حذف بشكل نهائي", + "delete_permanently_action_prompt": "تم حذف {count} بشكل نهائي", "delete_shared_link": "حذف الرابط المشترك", "delete_shared_link_dialog_title": "حذف الرابط المشترك", "delete_tag": "حذف العلامة", @@ -760,6 +771,7 @@ "description": "وصف", "description_input_hint_text": "اضف وصفا...", "description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل", + "deselect_all": "الغاء تحديد الكل", "details": "تفاصيل", "direction": "الإتجاه", "disabled": "معطل", @@ -777,6 +789,7 @@ "documentation": "الوثائق", "done": "تم", "download": "تنزيل", + "download_action_prompt": "يتم تنزيل {count} ملف", "download_canceled": "الغي التنزيل", "download_complete": "اكتمل التنزيل", "download_enqueue": "تنزيل في قائمة الانتظار", @@ -833,6 +846,7 @@ "empty_trash": "أفرغ سلة المهملات", "empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!", "enable": "تفعيل", + "enable_backup": "تشغيل النسخ الاحتياطي", "enable_biometric_auth_description": "أدخل رمز PIN الخاص بك لتمكين المصادقة البيومترية", "enabled": "مفعل", "end_date": "تاريخ الإنتهاء", @@ -989,6 +1003,8 @@ "explorer": "المستكشف", "export": "تصدير", "export_as_json": "تصدير كـ JSON", + "export_database": "تصدير قاعدة البيانات", + "export_database_description": "تصدير قاعدة البيانات من نوع SQLite", "extension": "الإمتداد", "external": "خارجي", "external_libraries": "المكتبات الخارجية", @@ -1147,7 +1163,6 @@ "light": "المضيئ", "like_deleted": "تم حذف الإعجاب", "link_motion_video": "رابط فيديو الحركة", - "link_options": "خيارات الرابط", "link_to_oauth": "الربط مع OAuth", "linked_oauth_account": "حساب مرتبط بـ OAuth", "list": "قائمة", @@ -1210,7 +1225,6 @@ "manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها", "manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك", "map": "الخريطة", - "map_assets_in_bound": "{count} صوره", "map_assets_in_bounds": "{count} صور", "map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم", "map_location_dialog_yes": "نعم", diff --git a/i18n/be.json b/i18n/be.json index d3f59b32a5..669ec09849 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -6,7 +6,7 @@ "action": "Дзеянне", "action_common_update": "Абнавіць", "actions": "Дзеянні", - "active": "Актыўны", + "active": "Актыўных", "activity": "Актыўнасць", "activity_changed": "Актыўнасць {enabled, select, true {уключана} other {адключана}}", "add": "Дадаць", @@ -74,8 +74,11 @@ "image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.", "image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.", "image_fullsize_title": "Налады выявы ў поўным памеры", + "image_prefer_embedded_preview": "Аддаваць перавагу ўбудаванай праяве", "image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.", "image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме", + "image_preview_description": "Відарыс сярэдняга памеру з выдаленымі метададзенымі, выкарыстоўваецца пры праглядзе асобнага рэсурсу і для машыннага навучання", + "image_preview_quality_description": "Якасць праявы ад 1 да 100. Чым вышэй, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання. Ўстаноўка нізкага значэння можа паўплываць на якасць машыннага навучання.", "image_preview_title": "Налады папярэдняга прагляду", "image_quality": "Якасць", "image_resolution": "Раздзяляльнасць", @@ -93,33 +96,119 @@ "metadata_settings": "Налады метаданых", "oauth_button_text": "Тэкст кнопкі", "oauth_settings": "OAuth", + "refreshing_all_libraries": "Абнаўленне ўсіх бібліятэк", + "registration": "Рэгістрацыя адміністратара", + "registration_description": "Вы з'яўляецеся першым карыстальнікам сістэмы, таму вы будзеце прызначаны адміністратарам. Вы будзеце адказваць за адміністрацыйныя задачы, а таксама ствараць новых карыстальнікаў.", + "require_password_change_on_login": "Патрабаваць змяніць пароль пры першым уваходзе ў сістэму", + "reset_settings_to_default": "Скінуць налады да прадвызначаных", + "reset_settings_to_recent_saved": "Скінуць налады да нядаўна захаваных", + "scanning_library": "Сканіраванне бібліятэкі", + "server_external_domain_settings": "Знешні дамен", + "server_settings": "Налады сервера", + "server_settings_description": "Кіраванне наладамі сервера", + "server_welcome_message": "Прывітальнае паведамленне", + "server_welcome_message_description": "Паведамленне, якое адлюстроўваецца на старонцы ўваходу.", "system_settings": "Сістэмныя налады", + "tag_cleanup_job": "Ачыстка тэгаў", + "template_email_preview": "Перадпрагляд", "theme_settings": "Налады тэмы", + "transcoding_acceleration_nvenc": "NVENC (патрабуецца відэакарта NVIDIA)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_containers": "Прынятыя кантэйнеры", + "transcoding_accepted_video_codecs": "Прынятыя відэакодэкі", + "transcoding_advanced_options_description": "Параметры, якія большасці карыстальнікаў не трэба змяняць", "transcoding_audio_codec": "Аудыякодэк", + "transcoding_encoding_options": "Параметры кадзіравання", "transcoding_video_codec": "Відэакодэк", + "trash_enabled_description": "Уключыць функцыі сметніцы", + "trash_number_of_days": "Колькасць дзён", "trash_settings": "Налады сметніцы", "trash_settings_description": "Кіраванне наладамі сметніцы", + "user_cleanup_job": "Ачыстка карыстальніка", + "user_management": "Кіраванне карыстальнікамі", + "user_password_has_been_reset": "Пароль карыстальніка быў скінуты:", + "user_password_reset_description": "Задайце карыстальніку часовы пароль і паведаміце яму, што пры наступным уваходзе ў сістэму яму трэба будзе змяніць пароль.", + "user_restore_description": "Уліковы запіс карыстальніка {user} будзе адноўлены.", + "user_settings": "Налады карыстальніка", + "user_settings_description": "Кіраванне наладамі карыстальніка", + "user_successfully_removed": "Карыстальнік {email} быў паспяхова выдалены.", + "version_check_enabled_description": "Уключыць праверку версіі", + "version_check_implications": "Функцыі праверкі версіі перыядычна звяртаецца да github.com", "version_check_settings": "Праверка версіі", "version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі" }, + "admin_email": "Электронная пошта адміністратара", + "admin_password": "Пароль адміністратара", + "administration": "Кіраванне серверам", + "advanced": "Пашыраныя", + "advanced_settings_log_level_title": "Узровень вядзення журнала: {level}", + "advanced_settings_proxy_headers_title": "Загалоўкі проксі", + "advanced_settings_tile_subtitle": "Пашыраныя налады карыстальніка", + "advanced_settings_troubleshooting_subtitle": "Уключыць дадатковыя функцыі для выпраўлення непаладак", "advanced_settings_troubleshooting_title": "Выпраўленне непаладак", + "age_months": "Узрост {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_year_months": "Узрост 1 год, {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_years": "{years, plural, other {Узрост #}}", "album_added": "Альбом дададзены", + "album_cover_updated": "Вокладка альбома абноўлена", + "album_delete_confirmation": "Вы ўпэўнены, што хочаце выдаліць альбом {album}?", + "album_delete_confirmation_description": "Калі гэты альбом абагулены, іншыя карыстальнікі больш не змогуць атрымаць да яго доступ.", + "album_deleted": "Альбом выдалены", + "album_info_card_backup_album_excluded": "ВЫКЛЮЧАНЫ", + "album_info_card_backup_album_included": "УКЛЮЧАНЫ", + "album_info_updated": "Інфармацыя пра альбом абноўлена", + "album_leave": "Пакінуць альбом?", + "album_leave_confirmation": "Вы ўпэўнены, што хочаце пакінуць {album}?", "album_name": "Назва альбома", + "album_options": "Параметры альбома", "album_remove_user": "Выдаліць карыстальніка?", + "album_remove_user_confirmation": "Вы ўпэўнены, што хочаце выдаліць {user}?", + "album_search_not_found": "Па вашым запыце не знойдзена альбомаў", + "album_share_no_users": "Здаецца, вы падзяліліся гэтым альбомам з усімі карыстальнікамі, або ў вас няма ніводнага карыстальніка, з якім можна падзяліцца.", "album_updated": "Альбом абноўлены", + "album_user_left": "Вы пакінулі {album}", + "album_user_removed": "Карыстальнік {user} выдалены", + "album_viewer_appbar_delete_confirm": "Вы ўпэўнены, што хочаце выдаліць гэты альбом са свайго ўліковага запісу?", + "album_viewer_appbar_share_err_delete": "Не ўдалося выдаліць альбом", + "album_viewer_appbar_share_err_leave": "Не ўдалося пакінуць альбом", + "album_viewer_appbar_share_err_title": "Не ўдалося змяніць назву альбома", + "album_viewer_appbar_share_leave": "Пакінуць альбом", + "album_viewer_appbar_share_to": "Абагуліць з", + "album_viewer_page_share_add_users": "Дадаць карыстальнікаў", + "album_with_link_access": "Дазволіць усім, хто мае спасылку, бачыць фота і людзей у гэтым альбоме.", "albums": "Альбомы", + "albums_count": "{count, plural, one {1 альбом} few {{count, number} альбомы} many {{count, number} альбомаў} other {{count, number} альбомаў}}", + "albums_default_sort_order": "Прадвызначаны парадак сартавання альбомаў", + "albums_on_device_count": "Альбомы на прыладзе ({count})", "all": "Усе", "all_albums": "Усе альбомы", "all_people": "Усе людзі", "all_videos": "Усе відэа", + "allow_dark_mode": "Дазволіць цёмны рэжым", + "allow_edits": "Дазволіць рэдагаванне", + "alt_text_qr_code": "Відарыс QR-кода", + "anti_clockwise": "Супраць гадзіннікавай стрэлкі", + "api_key": "Ключ API", + "api_key_empty": "Назва ключа API не павінна быць пустой", + "api_keys": "Ключы API", + "app_bar_signout_dialog_content": "Вы ўпэўнены, што хочаце выйсці?", "app_bar_signout_dialog_ok": "Так", "app_bar_signout_dialog_title": "Выйсці", "app_settings": "Налады праграмы", "archive": "Архіў", + "archive_page_title": "Архіў ({count})", "archive_size": "Памер архіва", + "are_these_the_same_person": "Ці гэта адзін і той жа чалавек?", + "are_you_sure_to_do_this": "Вы ўпэўнены, што хочаце гэта зрабіць?", + "asset_added_to_album": "Дададзена ў альбом", + "asset_adding_to_album": "Дадаванне ў альбом…", + "asset_skipped": "Прапушчана", + "asset_skipped_in_trash": "У сметніцы", + "asset_uploaded": "Запампавана", "asset_uploading": "Запампоўванне…", + "authorized_devices": "Аўтарызаваныя прылады", "back": "Назад", + "backup_album_selection_page_albums_device": "Альбомы на прыладзе ({count})", "backup_all": "Усе", "backup_controller_page_background_wifi": "Толькі праз Wi-Fi", "buy": "Купіць Immich", diff --git a/i18n/bg.json b/i18n/bg.json index 327fd7c7df..cb5cc437b6 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -508,6 +508,7 @@ "back_close_deselect": "Назад, затваряне или премахване на избора", "background_location_permission": "Разрешение за достъп до местоположението във фонов режим", "background_location_permission_content": "За да може да чете имената на Wi-Fi мрежите и да ги превключва при работа във фонов режим, Immich трябва *винаги* да има достъп до точното местоположение", + "backup": "Архивиране", "backup_album_selection_page_albums_device": "Албуми на устройството ({count})", "backup_album_selection_page_albums_tap": "Натисни за да включиш, двойно за да изключиш", "backup_album_selection_page_assets_scatter": "Обектите могат да бъдат разпръснати в няколко албума. По този начин албумите могат да бъдат включени или изключени по време на процеса на архивиране.", @@ -1154,7 +1155,6 @@ "light": "Светло", "like_deleted": "Като изтрит", "link_motion_video": "Линк към видео", - "link_options": "Опции на линк за споделяне", "link_to_oauth": "Линк към OAuth", "linked_oauth_account": "Свързан OAuth акаунт", "list": "Лист", @@ -1217,7 +1217,6 @@ "manage_your_devices": "Управление на влезлите в системата устройства", "manage_your_oauth_connection": "Управление на OAuth връзката", "map": "Карта", - "map_assets_in_bound": "{count} снимки", "map_assets_in_bounds": "{count} снимки", "map_cannot_get_user_location": "Не можах да получа местоположението", "map_location_dialog_yes": "Да", diff --git a/i18n/bn.json b/i18n/bn.json index d71e4e25ae..9dce989af4 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -63,7 +63,7 @@ "exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।", "external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা", "face_detection": "মুখ সনাক্তকরণ", - "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখগুলি সনাক্ত করুন। ভিডিওগুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", + "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখ/চেহারা গুলি সনাক্ত করুন। ভিডিও গুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" করার মাধ্যমে অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", "facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।", "failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}", "force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।", diff --git a/i18n/ca.json b/i18n/ca.json index 39c249c3a3..d24481f077 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -496,6 +496,7 @@ "back_close_deselect": "Tornar, tancar o anul·lar la selecció", "background_location_permission": "Permís d'ubicació en segon pla", "background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accés a la ubicació precisa perquè l'aplicació pugui llegir el nom de la xarxa Wi-Fi", + "backup": "Còpia", "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})", "backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure", "backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procés de còpia de seguretat.", @@ -1139,7 +1140,6 @@ "light": "Llum", "like_deleted": "M'agrada suprimit", "link_motion_video": "Enllaçar vídeo en moviment", - "link_options": "Opcions d'enllaç", "link_to_oauth": "Enllaç a OAuth", "linked_oauth_account": "Compte OAuth enllaçat", "list": "Llista", @@ -1202,7 +1202,6 @@ "manage_your_devices": "Gestioneu els vostres dispositius connectats", "manage_your_oauth_connection": "Gestioneu la vostra connexió OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "No es pot obtenir la ubicació de l'usuari", "map_location_dialog_yes": "Sí", diff --git a/i18n/cs.json b/i18n/cs.json index 80ea9674c0..c5e5ac9553 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -14,6 +14,7 @@ "add_a_location": "Přidat polohu", "add_a_name": "Přidat jméno", "add_a_title": "Přidat název", + "add_birthday": "Přidat datum narození", "add_endpoint": "Přidat koncový bod", "add_exclusion_pattern": "Přidat vzor vyloučení", "add_import_path": "Přidat cestu importu", @@ -44,6 +45,13 @@ "backup_database": "Vytvořit výpis databáze", "backup_database_enable_description": "Povolit výpisy z databáze", "backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat", + "backup_onboarding_1_description": "kopie v cloudu nebo na jiném fyzickém místě.", + "backup_onboarding_2_description": "místní kopie na různých zařízeních. To zahrnuje hlavní soubory a jejich místní zálohu.", + "backup_onboarding_3_description": "kompletní kopie vašich dat, včetně původních souborů. To zahrnuje 1 kopii na jiném místě a 2 místní kopie.", + "backup_onboarding_description": "K ochraně vašich dat doporučujeme strategii zálohování 3-2-1. Pro komplexní zálohování byste měli uchovávat kopie nahraných fotografií/videí i databáze Immich.", + "backup_onboarding_footer": "Další informace o zálohování Immiche naleznete v dokumentaci.", + "backup_onboarding_parts_title": "Záloha 3-2-1 zahrnuje:", + "backup_onboarding_title": "Zálohy", "backup_settings": "Zálohování databáze", "backup_settings_description": "Správa nastavení výpisu databáze.", "cleared_jobs": "Hotové úlohy pro: {job}", @@ -374,7 +382,7 @@ "administration": "Administrace", "advanced": "Pokročilé", "advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace", - "advanced_settings_beta_timeline_title": "Časová osa beta verze", + "advanced_settings_beta_timeline_title": "Beta verze časové osy", "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení", "advanced_settings_log_level_title": "Úroveň protokolování: {level}", @@ -397,6 +405,7 @@ "album_cover_updated": "Obal alba aktualizován", "album_delete_confirmation": "Opravdu chcete album {album} odstranit?", "album_delete_confirmation_description": "Pokud je toto album sdíleno, ostatní uživatelé k němu již nebudou mít přístup.", + "album_deleted": "Album smazáno", "album_info_card_backup_album_excluded": "VYLOUČENO", "album_info_card_backup_album_included": "ZAHRNUTO", "album_info_updated": "Informace o albu aktualizovány", @@ -510,6 +519,7 @@ "back_close_deselect": "Zpět, zavřít nebo zrušit výběr", "background_location_permission": "Povolení polohy na pozadí", "background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě", + "backup": "Záloha", "backup_album_selection_page_albums_device": "Alba v zařízení ({count})", "backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.", @@ -573,7 +583,7 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", - "beta_sync": "Stav synchronizace beta verze", + "beta_sync": "Stav synchronizace (beta)", "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", @@ -723,6 +733,7 @@ "current_server_address": "Aktuální adresa serveru", "custom_locale": "Vlastní lokalizace", "custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti", + "custom_url": "Vlastní URL", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", @@ -742,7 +753,8 @@ "default_locale": "Výchozí jazyk", "default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče", "delete": "Smazat", - "delete_action_prompt": "{count} trvale smazaných", + "delete_action_confirmation_message": "Opravdu chcete odstranit tuto položku? Tato akce přesune položku do serverového koše a zeptá se vás, zda ji chcete odstranit lokálně", + "delete_action_prompt": "{count} smazáno", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení", @@ -759,7 +771,9 @@ "delete_local_action_prompt": "{count} smazáno lokálně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované", "delete_local_dialog_ok_force": "Přesto smazat", - "delete_others": "Odstranit ostatní", + "delete_others": "Smazat ostatní", + "delete_permanently": "Trvale smazat", + "delete_permanently_action_prompt": "{count} trvale smazáno", "delete_shared_link": "Smazat sdílený odkaz", "delete_shared_link_dialog_title": "Odstranit sdílený odkaz", "delete_tag": "Smazat značku", @@ -815,6 +829,7 @@ "edit": "Upravit", "edit_album": "Upravit album", "edit_avatar": "Upravit avatar", + "edit_birthday": "Upravit datum narození", "edit_date": "Upravit datum", "edit_date_and_time": "Upravit datum a čas", "edit_description": "Upravit popis", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Přidat popis...", + "exif_bottom_sheet_description_error": "Chyba při aktualizaci popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "LIDÉ", @@ -1002,6 +1018,8 @@ "explorer": "Průzkumník", "export": "Export", "export_as_json": "Exportovat jako JSON", + "export_database": "Exportovat databázi", + "export_database_description": "Exportovat databázi SQLite", "extension": "Přípona", "external": "Externí", "external_libraries": "Externí knihovny", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Nebyly nalezeny žádné jazyky", "language_search_hint": "Vyhledat jazyk...", "language_setting_description": "Vyberte upřednostňovaný jazyk", + "large_files": "Velké soubory", "last_seen": "Naposledy viděno", "latest_version": "Nejnovější verze", "latitude": "Zeměpisná šířka", @@ -1165,7 +1184,6 @@ "light": "Světlý", "like_deleted": "Lajk smazán", "link_motion_video": "Připojit pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Propojit s OAuth", "linked_oauth_account": "Propojený OAuth účet", "list": "Seznam", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Správa přihlášených zařízení", "manage_your_oauth_connection": "Správa OAuth propojení", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few{# fotky} other {# fotek}}", "map_cannot_get_user_location": "Nelze zjistit polohu uživatele", "map_location_dialog_yes": "Ano", "map_location_picker_page_use_location": "Použít tuto polohu", @@ -1582,6 +1599,7 @@ "resume": "Pokračovat", "retry_upload": "Opakování nahrávání", "review_duplicates": "Kontrola duplicit", + "review_large_files": "Kontrola velkých souborů", "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Zkopírováno do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Chyba při vytváření sdíleného odkazu", + "shared_link_custom_url_description": "Přístup k tomuto sdílenému odkazu pomocí vlastního URL", "shared_link_edit_description_hint": "Zadejte popis sdílení", "shared_link_edit_expire_after_option_day": "1 den", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovat sdílené odkazy", "shared_link_options": "Možnosti sdíleného odkazu", + "shared_link_password_description": "Vyžadovat heslo pro přístup k tomuto sdílenému odkazu", "shared_links": "Sdílené odkazy", "shared_links_description": "Sdílet fotky a videa pomocí odkazu", "shared_photos_and_videos_count": "{assetCount, plural, one {# sdílená fotografie a video.} few {# sdílené fotografie a videa.} other {# sdílených fotografií a videí.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualizováno", "updated_password": "Heslo aktualizováno", "upload": "Nahrát", + "upload_action_prompt": "{count} ve frontě pro nahrání", "upload_concurrency": "Souběžnost nahrávání", "upload_details": "Detaily nahrávání", "upload_dialog_info": "Chcete zálohovat vybrané položky na server?", "upload_dialog_title": "Nahrát položku", "upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.", + "upload_finished": "Nahrávání dokončeno", "upload_progress": "Zbývá {remaining, number} - Zpracováno {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Přeskočena # duplicitní položka} few {Přeskočeny # duplicitní položky} other {Přeskočeno # duplicitních položek}}", "upload_status_duplicates": "Duplicity", @@ -1954,6 +1976,7 @@ "upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.", "upload_to_immich": "Nahrát do Immich ({count})", "uploading": "Nahrávání", + "uploading_media": "Nahrávání médií", "url": "URL", "usage": "Využití", "use_biometric": "Použít biometrické údaje", diff --git a/i18n/da.json b/i18n/da.json index e9f9534927..4e9846791b 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -167,6 +167,19 @@ "migration_job": "Migrering", "migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter", + "nightly_tasks_cluster_new_faces_setting": "Gruppér nye ansigter", + "nightly_tasks_database_cleanup_setting": "Databaseoprydning opgaver", + "nightly_tasks_database_cleanup_setting_description": "Ryd op i gamle, udløbne data fra databasen", + "nightly_tasks_generate_memories_setting": "Generer minder", + "nightly_tasks_generate_memories_setting_description": "Skab nye minder ud fra dine medier", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniaturebilleder", + "nightly_tasks_missing_thumbnails_setting_description": "Sæt medier uden thumbnails i kø til generering af thumbnails", + "nightly_tasks_settings": "Indstillinger for opgaver om natten", + "nightly_tasks_settings_description": "Administrér opgaver om natten", + "nightly_tasks_start_time_setting": "Starttidspunkt", + "nightly_tasks_start_time_setting_description": "Tidspunktet hvor serveren begynder at køre opgaver om natten", + "nightly_tasks_sync_quota_usage_setting": "Synkronisér kvoteforbrug", + "nightly_tasks_sync_quota_usage_setting_description": "Opdater brugerens lagerkvote baseret på aktuelt forbrug", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør", @@ -205,7 +218,7 @@ "oauth_storage_quota_claim": "Lagringskvotefordring", "oauth_storage_quota_claim_description": "Sæt automatisk brugeres lagringskvote til denne nye fordrings værdi.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", - "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring (Indtast 0 for uendelig kvote).", + "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring.", "oauth_timeout": "Forespørgslen udløb", "oauth_timeout_description": "Udløbstid for forespørgsel i milisekunder", "password_enable_description": "Log ind med email og adgangskode", @@ -489,6 +502,7 @@ "back_close_deselect": "Tilbage, luk eller fravælg", "background_location_permission": "Tilladelse til baggrundsplacering", "background_location_permission_content": "For at skifte netværk, når appen kører i baggrunden, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn", + "backup": "Sikkerhedskopier", "backup_album_selection_page_albums_device": "Albummer på enheden ({count})", "backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere", "backup_album_selection_page_assets_scatter": "Elementer kan være spredt på tværs af flere albummer. Albummer kan således inkluderes eller udelukkes under sikkerhedskopieringsprocessen.", @@ -1128,7 +1142,6 @@ "light": "Lys", "like_deleted": "Ligesom slettet", "link_motion_video": "Link bevægelsesvideo", - "link_options": "Link-indstillinger", "link_to_oauth": "Link til OAuth", "linked_oauth_account": "Tilsluttet OAuth-konto", "list": "Liste", @@ -1190,7 +1203,6 @@ "manage_your_devices": "Administrér dine enheder der er logget ind", "manage_your_oauth_connection": "Administrér din OAuth-tilslutning", "map": "Kort", - "map_assets_in_bound": "{count} billede", "map_assets_in_bounds": "{count} billeder", "map_cannot_get_user_location": "Kan ikke finde brugerens placering", "map_location_dialog_yes": "Ja", diff --git a/i18n/de.json b/i18n/de.json index 45245b182b..651f68792e 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -14,6 +14,7 @@ "add_a_location": "Standort hinzufügen", "add_a_name": "Name hinzufügen", "add_a_title": "Titel hinzufügen", + "add_birthday": "Geburtsdatum hinzufügen", "add_endpoint": "Endpunkt hinzufügen", "add_exclusion_pattern": "Ausschlussmuster hinzufügen", "add_import_path": "Importpfad hinzufügen", @@ -41,9 +42,16 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbanksicherung regelmäßig erstellen", + "backup_database": "Datenbanksicherung erstellen", "backup_database_enable_description": "Datenbank regelmäßig sichern", "backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Backups", + "backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.", + "backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.", + "backup_onboarding_3_description": "3 komplette Kopien deiner Daten, inkl. der Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopie.", + "backup_onboarding_description": "Eine 3-2-1 Backup-Strategie wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Backup-Lösung zu haben.", + "backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der Dokumentation.", + "backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:", + "backup_onboarding_title": "Backups", "backup_settings": "Einstellungen für Datenbanksicherung", "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}", @@ -120,7 +128,7 @@ "machine_learning_duplicate_detection_setting_description": "Verwendung von CLIP-Embeddings zum Erkennen möglicher Duplikate", "machine_learning_enabled": "Maschinelles Lernen aktivieren", "machine_learning_enabled_description": "Wenn diese Option deaktiviert ist, werden alle ML-Funktionen unabhängig von den unten aufgeführten Einstellungen deaktiviert.", - "machine_learning_facial_recognition": "Gesichtsidentifikation", + "machine_learning_facial_recognition": "Gesichtsidentifizierung", "machine_learning_facial_recognition_description": "Erkenne, identifiziere und gruppiere Gesichter in Bildern", "machine_learning_facial_recognition_model": "Gesichtserkennungs-Modell", "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer Größe aufgeführt. Größere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du die Gesichtserkennungsaufgabe für alle Bilder neu starten musst, wenn du ein Modell änderst.", @@ -385,7 +393,7 @@ "advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.", "advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben", "advanced_settings_sync_remote_deletions_subtitle": "Automatisches Löschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgeführt wird", - "advanced_settings_sync_remote_deletions_title": "Synchrone Remote-Löschungen [Experimentell]", + "advanced_settings_sync_remote_deletions_title": "Mit Server-Löschungen synchronisieren [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -397,6 +405,7 @@ "album_cover_updated": "Album-Cover aktualisiert", "album_delete_confirmation": "Bist du sicher, dass du das Album {album} löschen willst?", "album_delete_confirmation_description": "Falls dieses Album geteilt wurde, können andere Benutzer nicht mehr darauf zugreifen.", + "album_deleted": "Album gelöscht", "album_info_card_backup_album_excluded": "AUSGESCHLOSSEN", "album_info_card_backup_album_included": "EINGESCHLOSSEN", "album_info_updated": "Album-Infos aktualisiert", @@ -510,6 +519,7 @@ "back_close_deselect": "Zurück, Schließen oder Abwählen", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", + "backup": "Sicherung", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", @@ -573,7 +583,7 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", - "beta_sync": "Status des Beta Sync", + "beta_sync": "Status der Beta-Synchronisierung", "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", @@ -723,6 +733,7 @@ "current_server_address": "Aktuelle Serveradresse", "custom_locale": "Benutzerdefinierte Sprache", "custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren", + "custom_url": "Benutzerdefinierte URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", @@ -742,7 +753,8 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "Löschen", - "delete_action_prompt": "{count} endgültig gelöscht", + "delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt löschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal löschen willst", + "delete_action_prompt": "{count} gelöscht", "delete_album": "Album löschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen", "delete_local_dialog_ok_force": "Trotzdem löschen", "delete_others": "Andere löschen", + "delete_permanently": "Endgültig löschen", + "delete_permanently_action_prompt": "{count} endgültig gelöscht", "delete_shared_link": "geteilten Link löschen", "delete_shared_link_dialog_title": "Geteilten Link löschen", "delete_tag": "Tag löschen", @@ -815,6 +829,7 @@ "edit": "Bearbeiten", "edit_album": "Album bearbeiten", "edit_avatar": "Avatar bearbeiten", + "edit_birthday": "Geburtsdatum bearbeiten", "edit_date": "Datum bearbeiten", "edit_date_and_time": "Datum und Uhrzeit bearbeiten", "edit_description": "Beschreibung bearbeiten", @@ -982,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Beschreibung hinzufügen...", + "exif_bottom_sheet_description_error": "Fehler bei der Aktualisierung der Beschreibung", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", "exif_bottom_sheet_people": "PERSONEN", @@ -1002,6 +1018,8 @@ "explorer": "Datei-Explorer", "export": "Exportieren", "export_as_json": "Als JSON exportieren", + "export_database": "Datenbank exportieren", + "export_database_description": "Exportiert die SQLite Datenbank", "extension": "Erweiterung", "external": "Extern", "external_libraries": "Externe Bibliotheken", @@ -1077,7 +1095,7 @@ "home_page_archive_err_partner": "Inhalte von Partnern können nicht archiviert werden", "home_page_building_timeline": "Zeitachse wird erstellt", "home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden, überspringe", - "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, überspringen", + "home_page_delete_remote_err_local": "Lokale Elemente in der Auswahl zum Entfernen von Remote-Elementen, Überspringe", "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringen", "home_page_favorite_err_partner": "Inhalte von Partnern können nicht favorisiert werden, überspringe", "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Keine Sprachen gefunden", "language_search_hint": "Sprachen durchsuchen...", "language_setting_description": "Wähle deine bevorzugte Sprache", + "large_files": "Große Dateien", "last_seen": "Zuletzt gesehen", "latest_version": "Aktuellste Version", "latitude": "Breitengrad", @@ -1165,7 +1184,6 @@ "light": "Hell", "like_deleted": "Like gelöscht", "link_motion_video": "Bewegungsvideo verknüpfen", - "link_options": "Link-Optionen", "link_to_oauth": "Mit OAuth verknüpfen", "linked_oauth_account": "Verknüpftes OAuth-Konto", "list": "Liste", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-Verknüpfung verwalten", "map": "Karte", - "map_assets_in_bound": "{count} Foto", - "map_assets_in_bounds": "{count} Fotos", + "map_assets_in_bounds": "{count, plural, one {# Foto} other {# Fotos}}", "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", @@ -1529,8 +1546,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", - "remote": "Entfernt", - "remote_assets": "Entfernte Dateien", + "remote": "Server", + "remote_assets": "Server-Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1582,6 +1599,7 @@ "resume": "Fortsetzen", "retry_upload": "Upload wiederholen", "review_duplicates": "Duplikate überprüfen", + "review_large_files": "Große Dateien überprüfen", "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Link kopiert", "shared_link_clipboard_text": "Link: {link}\nPasswort: {password}", "shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe", + "shared_link_custom_url_description": "Greife über eine benutzerdefinierte URL auf diesen Freigabelink zu", "shared_link_edit_description_hint": "Beschreibung eingeben", "shared_link_edit_expire_after_option_day": "1 Tag", "shared_link_edit_expire_after_option_days": "{count} Tagen", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", "shared_link_options": "Optionen für geteilten Link", + "shared_link_password_description": "Für den Zugriff auf diesen freigegebenen Link ist ein Passwort erforderlich", "shared_links": "Geteilte Links", "shared_links_description": "Teile Fotos und Videos mit einem Link", "shared_photos_and_videos_count": "{assetCount, plural, one {# geteiltes Foto oder Video.} other {# geteilte Fotos & Videos.}}", @@ -1850,7 +1870,7 @@ "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", "sync_local": "Lokal synchronisieren", - "sync_remote": "Entfernt synchronisieren", + "sync_remote": "mit Server synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1866,7 +1886,7 @@ "theme": "Theme", "theme_selection": "Themenauswahl", "theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers", - "theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild", + "theme_setting_asset_list_storage_indicator_title": "Fortschrittsbalken der Sicherung auf dem Vorschaubild", "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})", "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", + "upload_action_prompt": "{count} in der Warteschlange für Upload", "upload_concurrency": "Parallelität beim Hochladen", "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", + "upload_finished": "Upload fertig", "upload_progress": "{remaining, number} verbleibend - {processed, number}/{total, number} verarbeitet", "upload_skipped_duplicates": "{count, plural, one {# doppelte Datei} other {# doppelte Dateien}} ausgelassen", "upload_status_duplicates": "Duplikate", @@ -1954,6 +1976,7 @@ "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", + "uploading_media": "Medien werden hochgeladen", "url": "URL", "usage": "Verwendung", "use_biometric": "Biometrie verwenden", diff --git a/i18n/el.json b/i18n/el.json index 015bf80c0a..d8d12bddd0 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Διαχείρηση ρυθμίσεων μεταδεδομένων", "migration_job": "Μεταφορά δεδομένων (Migration)", "migration_job_description": "Μεταφορά των εικονιδίων για αρχεία και πρόσωπα στην πιο πρόσφατη δομή αρχείων", + "nightly_tasks_cluster_faces_setting_description": "Εκτέλεση αναγνώρισης προσώπου σε νέα ανιχνευμένα πρόσωπα", + "nightly_tasks_cluster_new_faces_setting": "Ομαδοποίηση νέων προσώπων", + "nightly_tasks_database_cleanup_setting": "Εργασίες καθαρισμού βάσης δεδομένων", + "nightly_tasks_database_cleanup_setting_description": "Εκκαθάριση παλιών και ληγμένων δεδομένων από τη βάση δεδομένων", + "nightly_tasks_generate_memories_setting": "Δημιουργία αναμνήσεων", + "nightly_tasks_generate_memories_setting_description": "Δημιουργία νέων αναμνήσεων από αντικείμενα", + "nightly_tasks_missing_thumbnails_setting": "Δημιουργία ελλειπόντων μικρογραφιών", + "nightly_tasks_missing_thumbnails_setting_description": "Τοποθέτηση στη ουρά των αρχείων χωρίς μικρογραφίες για δημιουργία μικρογραφιών", + "nightly_tasks_settings": "Ρυθμίσεις για τις νυχτερινές εργασίες", + "nightly_tasks_settings_description": "Διαχείριση νυχτερινών εργασιών", + "nightly_tasks_start_time_setting": "Ώρα έναρξης", + "nightly_tasks_start_time_setting_description": "Η ώρα κατά την οποία ο διακομιστής ξεκινάει να εκτελεί τις νυχτερινές εργασίες", + "nightly_tasks_sync_quota_usage_setting": "Συγχρονισμός χρήσης χώρου", + "nightly_tasks_sync_quota_usage_setting_description": "Ενημέρωση του διαθέσιμου χώρου χρήστη, με βάση την τρέχουσα χρήση", "no_paths_added": "Δεν προστέθηκαν διαδρομές", "no_pattern_added": "Δεν προστέθηκε μοτίβο", "note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI Ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override": "Προσπέλαση URI ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override_description": "Ενεργοποιήστε το όταν ο πάροχος OAuth δεν επιτρέπει μια URI για κινητά, όπως το ''{callback}''", + "oauth_role_claim": "Ανάθεση ρόλου", + "oauth_role_claim_description": "Αυτόματη παραχώρηση πρόσβασης διαχειριστή με βάση την ύπαρξη αυτής της ανάθεσης. Η ανάθεση μπορεί να είναι είτε 'χρήστης' είτε 'διαχειριστής'.", "oauth_settings": "OAuth", "oauth_settings_description": "Διαχείριση ρυθμίσεων σύνδεσης OAuth", "oauth_settings_more_details": "Για περισσότερες λεπτομέρειες σχετικά με αυτήν τη δυνατότητα, ανατρέξτε στην τεκμηρίωση.", @@ -357,10 +373,12 @@ "admin_password": "Κωδικός πρόσβασης Διαχειριστή", "administration": "Διαχείριση", "advanced": "Για προχωρημένους", + "advanced_settings_beta_timeline_subtitle": "Δοκίμασε τη νέα εμπειρία της εφαρμογής", + "advanced_settings_beta_timeline_title": "Δοκιμαστικό χρονολόγιο", "advanced_settings_enable_alternate_media_filter_subtitle": "Χρησιμοποιήστε αυτήν την επιλογή για να φιλτράρετε τα μέσα ενημέρωσης κατά τον συγχρονισμό με βάση εναλλακτικά κριτήρια. Δοκιμάστε αυτή τη δυνατότητα μόνο αν έχετε προβλήματα με την εφαρμογή που εντοπίζει όλα τα άλμπουμ.", "advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Χρήση εναλλακτικού φίλτρου συγχρονισμού άλμπουμ συσκευής", "advanced_settings_log_level_title": "Επίπεδο σύνδεσης: {level}", - "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από αρχεία στη συσκευή. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", + "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από τοπικά αρχεία. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", "advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων", "advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου", "advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης", @@ -379,6 +397,7 @@ "album_cover_updated": "Το εξώφυλλο του άλμπουμ, ενημερώθηκε", "album_delete_confirmation": "Είστε σίγουροι ότι θέλετε να διαγράψετε το άλμπουμ {album};", "album_delete_confirmation_description": "Εάν αυτό το άλμπουμ είναι κοινόχρηστο, οι άλλοι χρήστες δεν θα μπορούν να έχουν πρόσβαση.", + "album_deleted": "Το άλμπουμ διαγράφηκε", "album_info_card_backup_album_excluded": "ΕΞΑΙΡΟΥΜΕΝΟ", "album_info_card_backup_album_included": "ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΟ", "album_info_updated": "Οι πληροφορίες του άλμπουμ, ενημερώθηκαν", @@ -388,6 +407,7 @@ "album_options": "Επιλογές άλμπουμ", "album_remove_user": "Διαγραφή χρήστη;", "album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};", + "album_search_not_found": "Δε βρέθηκαν άλμπουμ που να ταιριάζουν με την αναζήτησή σας", "album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.", "album_updated": "Το άλμπουμ, ενημερώθηκε", "album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Προεπιλεγμένη ταξινόμηση άλμπουμ", "albums_default_sort_order_description": "Αρχική ταξινόμηση κατά τη δημιουργία νέων άλμπουμ.", "albums_feature_description": "Συλλογές στοιχείων που μπορούν να κοινοποιηθούν σε άλλους χρήστες.", + "albums_on_device_count": "Άλμπουμ στη συσκευή ({count})", "all": "Όλα", "all_albums": "Όλα τα άλμπουμ", "all_people": "Όλα τα άτομα", @@ -427,6 +448,7 @@ "app_settings": "Ρυθμίσεις εφαρμογής", "appears_in": "Εμφανίζεται σε", "archive": "Αρχείο", + "archive_action_prompt": "Προστέθηκαν {count} στο Αρχείο", "archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας", "archive_page_no_archived_assets": "Δε βρέθηκαν αρχειοθετημένα στοιχεία", "archive_page_title": "Αρχείο ({count})", @@ -489,6 +511,7 @@ "back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή", "background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο", "background_location_permission_content": "Το Immich για να μπορεί να αλλάζει δίκτυα όταν τρέχει στο παρασκήνιο, πρέπει *πάντα* να έχει πρόσβαση στην ακριβή τοποθεσία ώστε η εφαρμογή να μπορεί να διαβάζει το όνομα του δικτύου Wi-Fi", + "backup": "Αντίγραφα ασφαλείας", "backup_album_selection_page_albums_device": "Άλμπουμ στη συσκευή ({count})", "backup_album_selection_page_albums_tap": "Πάτημα για συμπερίληψη, διπλό πάτημα για εξαίρεση", "backup_album_selection_page_assets_scatter": "Τα στοιχεία μπορεί να διασκορπιστούν σε πολλά άλμπουμ. Έτσι, τα άλμπουμ μπορούν να περιληφθούν ή να εξαιρεθούν κατά τη διαδικασία δημιουργίας αντιγράφων ασφαλείας.", @@ -552,6 +575,8 @@ "backup_options_page_title": "Επιλογές αντιγράφων ασφαλείας", "backup_setting_subtitle": "Διαχείριση ρυθμίσεων μεταφόρτωσης στο παρασκήνιο και στο προσκήνιο", "backward": "Προς τα πίσω", + "beta_sync": "Κατάσταση Συγχρονισμού Beta (δοκιμαστική)", + "beta_sync_subtitle": "Διαχείριση του νέου συστήματος συγχρονισμού", "biometric_auth_enabled": "Βιομετρική ταυτοποίηση ενεργοποιήθηκε", "biometric_locked_out": "Είστε κλειδωμένοι εκτός της βιομετρικής ταυτοποίησης", "biometric_no_options": "Δεν υπάρχουν διαθέσιμοι τρόποι βιομετρικής ταυτοποίησης", @@ -586,6 +611,7 @@ "cancel": "Ακύρωση", "cancel_search": "Ακύρωση αναζήτησης", "canceled": "Ακυρωμένο", + "canceling": "Ακυρώνεται", "cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων", "cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!", "cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής", @@ -699,9 +725,11 @@ "current_server_address": "Τρέχουσα διεύθυνση διακομιστή", "custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση", "custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή", + "custom_url": "Προσαρμοσμένη διεύθυνση URL", "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "Σκούρο", + "dark_theme": "Εναλλαγή σκοτεινής εμφάνισης", "date_after": "Ημερομηνία μετά", "date_and_time": "Ημερομηνία και ώρα", "date_before": "Ημερομηνία πριν", @@ -717,6 +745,8 @@ "default_locale": "Προεπιλεγμένη Τοπική Ρύθμιση", "default_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς με βάση την τοπική ρύθμιση του προγράμματος περιήγησής σας", "delete": "Διαγραφή", + "delete_action_confirmation_message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο; Αυτή η ενέργεια θα το μετακινήσει στον κάδο απορριμμάτων του διακομιστή και θα εμφανιστεί μήνυμα για το αν θέλετε να το διαγράψετε και τοπικά", + "delete_action_prompt": "{count} διαγράφηκαν", "delete_album": "Διαγραφή άλμπουμ", "delete_api_key_prompt": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό κλειδί API;", "delete_dialog_alert": "Αυτά τα αντικείμενα θα διαγραφούν οριστικά από το Immich και από τη συσκευή σας", @@ -730,9 +760,12 @@ "delete_key": "Διαγραφή κλειδιού", "delete_library": "Διαγραφή Βιβλιοθήκης", "delete_link": "Διαγραφή συνδέσμου", + "delete_local_action_prompt": "{count} διαγράφηκαν τοπικά", "delete_local_dialog_ok_backed_up_only": "Διαγραφή μόνο των αντιγράφων ασφαλείας", "delete_local_dialog_ok_force": "Διαγραφή όπως και να έχει", "delete_others": "Διαγραφή υπολοίπων", + "delete_permanently": "Διαγραφή οριστικά", + "delete_permanently_action_prompt": "{count} διαγράφηκε οριστικά", "delete_shared_link": "Διαγραφή κοινόχρηστου συνδέσμου", "delete_shared_link_dialog_title": "Διαγραφή Κοινοποιημένου Συνδέσμου", "delete_tag": "Διαγραφή ετικέτας", @@ -743,6 +776,7 @@ "description": "Περιγραφή", "description_input_hint_text": "Προσθήκη περιγραφής...", "description_input_submit_error": "Σφάλμα κατά την ενημέρωση της περιγραφής, ελέγξτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες", + "deselect_all": "Ακύρωση όλων των επιλογών", "details": "Λεπτομέρειες", "direction": "Κατεύθυνση", "disabled": "Απενεργοποιημένο", @@ -760,6 +794,7 @@ "documentation": "Τεκμηρίωση", "done": "Έγινε", "download": "Λήψη", + "download_action_prompt": "Κατέβασμα {count} στοιχείων", "download_canceled": "Η λήψη ακυρώθηκε", "download_complete": "Η λήψη ολοκληρώθηκε", "download_enqueue": "Η λήψη τέθηκε σε ουρά", @@ -797,6 +832,7 @@ "edit_key": "Επεξεργασία κλειδιού", "edit_link": "Επεξεργασία συνδέσμου", "edit_location": "Επεξεργασία τοποθεσίας", + "edit_location_action_prompt": "Επεξεργάστηκαν {count} τοποθεσίες", "edit_location_dialog_title": "Τοποθεσία", "edit_name": "Επεξεργασία ονόματος", "edit_people": "Επεξεργασία ατόμων", @@ -815,6 +851,7 @@ "empty_trash": "Άδειασμα κάδου απορριμμάτων", "empty_trash_confirmation": "Είστε σίγουροι οτι θέλετε να αδειάσετε τον κάδο απορριμμάτων; Αυτό θα αφαιρέσει μόνιμα όλα τα στοιχεία του κάδου απορριμμάτων του Immich. \nΑυτή η ενέργεια δεν μπορεί να αναιρεθεί!", "enable": "Ενεργοποίηση", + "enable_backup": "Ενεργοποίηση αντιγράφου ασφαλείας", "enable_biometric_auth_description": "Εισάγετε τον κωδικό PIN σας για να ενεργοποιήσετε την βιομετρική ταυτοποίηση", "enabled": "Ενεργοποιημένο", "end_date": "Τελική ημερομηνία", @@ -971,6 +1008,8 @@ "explorer": "Περιηγητής", "export": "Εξαγωγή", "export_as_json": "Εξαγωγή ως JSON", + "export_database": "Εξαγωγή βάσης δεδομένων", + "export_database_description": "Εξαγωγή της SQLite βάσης δεδομένων", "extension": "Επέκταση", "external": "Εξωτερικός", "external_libraries": "Εξωτερικές βιβλιοθήκες", @@ -982,6 +1021,7 @@ "failed_to_load_assets": "Αποτυχία φόρτωσης στοιχείων", "failed_to_load_folder": "Αποτυχία φόρτωσης φακέλου", "favorite": "Αγαπημένο", + "favorite_action_prompt": "Προστέθηκαν {count} στα Αγαπημένα", "favorite_or_unfavorite_photo": "Ορίστε μία φωτογραφία ως αγαπημένη ή αφαιρέστε την από τα αγαπημένα", "favorites": "Αγαπημένα", "favorites_page_no_favorites": "Δεν βρέθηκαν αγαπημένα στοιχεία", @@ -1021,6 +1061,9 @@ "haptic_feedback_switch": "Ενεργοποίηση απτικής ανάδρασης", "haptic_feedback_title": "Απτική Ανάδραση", "has_quota": "Έχει ποσόστωση", + "hash_asset": "Κατακερματισμός στοιχείου", + "hashed_assets": "Κατακερματισμένα στοιχεία", + "hashing": "Κατακερματισμός", "header_settings_add_header_tip": "Προσθήκη Κεφαλίδας", "header_settings_field_validator_msg": "Η τιμή δεν μπορεί να είναι κενή", "header_settings_header_name_input": "Όνομα κεφαλίδας", @@ -1053,6 +1096,7 @@ "host": "Φιλοξενία", "hour": "Ώρα", "id": "ID", + "idle": "Αδράνεια", "ignore_icloud_photos": "Αγνοήστε τις φωτογραφίες iCloud", "ignore_icloud_photos_description": "Οι φωτογραφίες που είναι αποθηκευμένες στο iCloud δεν θα μεταφορτωθούν στον διακομιστή Immich", "image": "Εικόνα", @@ -1110,6 +1154,7 @@ "language_no_results_title": "Δε βρέθηκαν γλώσσες", "language_search_hint": "Αναζήτηση γλωσσών...", "language_setting_description": "Επιλέξτε τη γλώσσα που προτιμάτε", + "large_files": "Μεγάλα Αρχεία", "last_seen": "Τελευταία προβολή", "latest_version": "Τελευταία Έκδοση", "latitude": "Γεωγραφικό πλάτος", @@ -1125,16 +1170,18 @@ "library_page_sort_created": "Ημερομηνία δημιουργίας", "library_page_sort_last_modified": "Τελευταία τροποποίηση", "library_page_sort_title": "Τίτλος άλμπουμ", + "licenses": "Άδειες", "light": "Φωτεινό", "like_deleted": "Το \"μου αρέσει\" διαγράφηκε", "link_motion_video": "Σύνδεσε βίντεο κίνησης", - "link_options": "Επιλογές συνδέσμου", "link_to_oauth": "Σύνδεση στον OAuth", "linked_oauth_account": "Ο OAuth λογαριασμός συνδέθηκε", "list": "Λίστα", "loading": "Φόρτωση", "loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε", + "local": "Τοπικά", "local_asset_cast_failed": "Αδυναμία μετάδοσης στοιχείου που δεν έχει ανέβει στον διακομιστή", + "local_assets": "Τοπικά στοιχεία", "local_network": "Τοπικό δίκτυο", "local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi", "location_permission": "Άδεια τοποθεσίας", @@ -1191,8 +1238,7 @@ "manage_your_devices": "Διαχειριστείτε τις συνδεδεμένες συσκευές σας", "manage_your_oauth_connection": "Διαχειριστείτε τη σύνδεσή σας OAuth", "map": "Χάρτης", - "map_assets_in_bound": "{count} φωτογραφία", - "map_assets_in_bounds": "{count} φωτογραφίες", + "map_assets_in_bounds": "{count, plural, one {# φωτογραφία} other {# φωτογραφίες}}", "map_cannot_get_user_location": "Δεν είναι δυνατή η λήψη της τοποθεσίας του χρήστη", "map_location_dialog_yes": "Ναι", "map_location_picker_page_use_location": "Χρησιμοποιήστε αυτήν την τοποθεσία", @@ -1244,6 +1290,7 @@ "more": "Περισσότερα", "move": "Μετακίνηση", "move_off_locked_folder": "Μετακίνηση έξω από τον κλειδωμένο φάκελο", + "move_to_lock_folder_action_prompt": "Προστέθηκαν {count} στον κλειδωμένο φάκελο", "move_to_locked_folder": "Μετακίνηση σε κλειδωμένο φάκελο", "move_to_locked_folder_confirmation": "Αυτές οι φωτογραφίες και τα βίντεο θα αφαιρεθούν από όλα τα άλμπουμ και θα μπορούν να προβληθούν μόνο από τον κλειδωμένο φάκελο", "moved_to_archive": "Μετακινήθηκαν {count, plural, one {# στοιχείο} other {# στοιχεία}} στο αρχείο", @@ -1290,6 +1337,7 @@ "no_results": "Κανένα αποτέλεσμα", "no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί", "no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας", + "no_uploads_in_progress": "Καμία μεταφόρτωση σε εξέλιξη", "not_in_any_album": "Σε κανένα άλμπουμ", "not_selected": "Δεν επιλέχθηκε", "note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το", @@ -1327,6 +1375,7 @@ "original": "πρωτότυπο", "other": "Άλλες", "other_devices": "Άλλες συσκευές", + "other_entities": "Άλλες οντότητες", "other_variables": "Άλλες μεταβλητές", "owned": "Δικά μου", "owner": "Κάτοχος", @@ -1458,6 +1507,7 @@ "purchase_server_description_2": "Κατάσταση υποστηρικτή", "purchase_server_title": "Διακομιστής", "purchase_settings_server_activated": "Η διαχείριση του κλειδιού προϊόντος του διακομιστή γίνεται από τον διαχειριστή", + "queue_status": "Τοποθέτηση στη ουρά {count} από {total}", "rating": "Αξιολόγηση με αστέρια", "rating_clear": "Εκκαθάριση αξιολόγησης", "rating_count": "{count, plural, one {# αστέρι} other {# αστέρια}}", @@ -1486,6 +1536,8 @@ "refreshing_faces": "Ανανεώνονται πρόσωπα", "refreshing_metadata": "Τα μεταδεδομένα ανανεώνονται", "regenerating_thumbnails": "Οι μικρογραφίες αναγεννώνται", + "remote": "Απομακρυσμένος", + "remote_assets": "Απομακρυσμένα στοιχεία", "remove": "Αφαίρεση", "remove_assets_album_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από το άλμπουμ;", "remove_assets_shared_link_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από αυτόν τον κοινόχρηστο σύνδεσμο;", @@ -1493,7 +1545,9 @@ "remove_custom_date_range": "Αφαίρεση προσαρμοσμένης χρονικής περιόδου", "remove_deleted_assets": "Αφαίρεση Διεγραμμένων Στοιχείων", "remove_from_album": "Αφαίρεση από το άλμπουμ", + "remove_from_album_action_prompt": "{count} αφαιρέθηκαν από το άλμπουμ", "remove_from_favorites": "Αφαίρεση από τα αγαπημένα", + "remove_from_lock_folder_action_prompt": "{count} αφαιρέθηκαν από τον κλειδωμένο φάκελο", "remove_from_locked_folder": "Αφαίρεση από κλειδωμένο φάκελο", "remove_from_locked_folder_confirmation": "Είστε σίγουροι ότι θέλετε να μετακινήσετε αυτές τις φωτογραφίες και τα βίντεο από τον κλειδωμένο φάκελο; Θα είναι πλέον ορατές στη βιβλιοθήκη σας.", "remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο", @@ -1521,19 +1575,25 @@ "reset_password": "Επαναφορά κωδικού πρόσβασης", "reset_people_visibility": "Επαναφορά προβολής ατόμων", "reset_pin_code": "Επαναφορά κωδικού PIN", + "reset_sqlite": "Επαναφορά SQLite βάσης δεδομένων", + "reset_sqlite_confirmation": "Είσαι σίγουρος ότι θέλεις να επαναφέρεις τη βάση δεδομένων SQLite; Θα χρειαστεί να κάνεις αποσύνδεση και επανασύνδεση για να επανασυγχρονίσεις τα δεδομένα", + "reset_sqlite_success": "Η επαναφορά της SQLite βάσης δεδομένων ολοκληρώθηκε με επιτυχία", "reset_to_default": "Επαναφορά στις προεπιλογές", "resolve_duplicates": "Επίλυση διπλοτύπων", "resolved_all_duplicates": "Επιλύθηκαν όλα τα διπλότυπα", "restore": "Ανάκτηση", "restore_all": "Ανάκτηση όλων", + "restore_trash_action_prompt": "{count} ανακτήθηκαν από τα απορρίμματα", "restore_user": "Επαναφορά χρήστη", "restored_asset": "Ανακτήθηκε το αρχείο", "resume": "Συνέχιση", "retry_upload": "Επανάληψη ανεβάσματος", "review_duplicates": "Προβολή διπλότυπων", + "review_large_files": "Επισκόπηση μεγάλων αρχείων", "role": "Ρόλος", "role_editor": "Επεξεργαστής", "role_viewer": "Θεατής", + "running": "Σε λειτουργία", "save": "Αποθήκευση", "save_to_gallery": "Αποθήκευση στη συλλογή", "saved_api_key": "Αποθηκευμένο API key", @@ -1665,6 +1725,7 @@ "settings_saved": "Οι ρυθμίσεις αποθηκεύτηκαν", "setup_pin_code": "Ρύθμιση κωδικού PIN", "share": "Κοινοποίηση", + "share_action_prompt": "Κοινή χρήση {count} στοιχείων", "share_add_photos": "Προσθήκη φωτογραφιών", "share_assets_selected": "{count} επιλεγμένα", "share_dialog_preparing": "Προετοιμασία...", @@ -1686,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Αντιγράφηκε στο πρόχειρο", "shared_link_clipboard_text": "Σύνδεσμος: {link}\nΚωδικός πρόσβασης: {password}", "shared_link_create_error": "Σφάλμα κατά τη δημιουργία κοινόχρηστου συνδέσμου", + "shared_link_custom_url_description": "Αποκτήστε πρόσβαση σε αυτόν τον κοινόχρηστο σύνδεσμο με μια προσαρμοσμένη διεύθυνση URL", "shared_link_edit_description_hint": "Εισαγάγετε την περιγραφή της κοινής χρήσης", "shared_link_edit_expire_after_option_day": "1 ημέρα", "shared_link_edit_expire_after_option_days": "{count} ημέρες", @@ -1711,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Διαχείριση Κοινόχρηστων Συνδέσμων", "shared_link_options": "Επιλογές κοινόχρηστου συνδέσμου", + "shared_link_password_description": "Απαίτηση κωδικού για πρόσβαση στον κοινοποιημένο σύνδεσμο", "shared_links": "Κοινόχρηστοι σύνδεσμοι", "shared_links_description": "Μοιραστείτε φωτογραφίες και βίντεο με σύνδεσμο", "shared_photos_and_videos_count": "{assetCount, plural, other {# κοινόχρηστες φωτογραφίες & βίντεο.}}", @@ -1766,6 +1829,7 @@ "sort_title": "Τίτλος", "source": "Πηγή", "stack": "Στοίβα", + "stack_action_prompt": "{count} συσσωρεύτηκαν", "stack_duplicates": "Στοίβαξη διπλότυπων", "stack_select_one_photo": "Επιλέξτε μια κύρια φωτογραφία για τη στοίβαξη", "stack_selected_photos": "Στοίβαγμα επιλεγμένων φωτογραφιών", @@ -1785,6 +1849,7 @@ "storage_quota": "Ποσοστό αποθηκευτικού χώρου", "storage_usage": "{used} από {available} σε χρήση", "submit": "Υποβολή", + "success": "Επιτυχία", "suggestions": "Προτάσεις", "sunrise_on_the_beach": "Ηλιοβασίλεμα στην παραλία", "support": "Υποστήριξη", @@ -1794,6 +1859,8 @@ "sync": "Συγχρονισμός", "sync_albums": "Συγχρονισμός άλμπουμ", "sync_albums_manual_subtitle": "Συγχρονίστε όλα τα μεταφορτωμένα βίντεο και φωτογραφίες με τα επιλεγμένα εφεδρικά άλμπουμ", + "sync_local": "Τοπικός Συγχρονισμός", + "sync_remote": "Απομακρυσμένος Συγχρονισμός", "sync_upload_album_setting_subtitle": "Δημιουργήστε και ανεβάστε τις φωτογραφίες και τα βίντεό σας στα επιλεγμένα άλμπουμ στο Immich", "tag": "Ετικέτα", "tag_assets": "Ετικετοποίηση στοιχείων", @@ -1804,6 +1871,7 @@ "tag_updated": "Ενημερώθηκε η ετικέτα: {tag}", "tagged_assets": "Ετικετοποιημένο/α {count, plural, one {# στοιχείο} other {# στοιχεία}}", "tags": "Ετικέτες", + "tap_to_run_job": "Πατήστε για να ξεκινήσει η εργασία", "template": "Πρότυπο", "theme": "Θέμα", "theme_selection": "Επιλογή θέματος", @@ -1836,6 +1904,7 @@ "total": "Σύνολο", "total_usage": "Συνολικη χρηση", "trash": "Κάδος απορριμμάτων", + "trash_action_prompt": "{count} μετακινήθηκαν στα απορρίμματα", "trash_all": "Διαγραφή Όλων", "trash_count": "Διαγραφή {count, number}", "trash_delete_asset": "Διαγραφή/Οριστ. Διαγραφή Αντικειμένου", @@ -1853,9 +1922,11 @@ "unable_to_change_pin_code": "Αδυναμία αλλαγής κωδικού PIN", "unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN", "unarchive": "Αναίρεση αρχειοθέτησης", + "unarchive_action_prompt": "{count} αφαιρέθηκαν από το Αρχείο", "unarchived_count": "{count, plural, other {Αρχειοθετήσεις αναιρέθηκαν #}}", "undo": "Αναίρεση", "unfavorite": "Αποεπιλογή από τα αγαπημένα", + "unfavorite_action_prompt": "{count} αφαιρέθηκαν από τα Αγαπημένα", "unhide_person": "Αναίρεση απόκρυψης ατόμου", "unknown": "Άγνωστο", "unknown_country": "Άγνωστη Χώρα", @@ -1873,15 +1944,20 @@ "unselect_all_duplicates": "Αποεπιλογή όλων των διπλότυπων", "unselect_all_in": "Αποεπιλογή όλων στο {group}", "unstack": "Αποστοίβαξη", + "unstack_action_prompt": "{count} αποσυσσωρεύτηκαν", "unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}", + "untagged": "Χωρίς ετικέτα", "up_next": "Ακολουθεί", "updated_at": "Ενημερωμένο", "updated_password": "Ο κωδικός πρόσβασης ενημερώθηκε", "upload": "Μεταφόρτωση", + "upload_action_prompt": "{count} τοποθετήθηκαν στην ουρά για μεταφόρτωση", "upload_concurrency": "Ταυτόχρονη μεταφόρτωση", + "upload_details": "Λεπτομέρειες μεταφόρτωσης", "upload_dialog_info": "Θέλετε να αντιγράψετε (κάνετε backup) τα επιλεγμένo(α) στοιχείο(α) στο διακομιστή;", "upload_dialog_title": "Ανέβασμα στοιχείου", "upload_errors": "Η μεταφόρτωση ολοκληρώθηκε με {count, plural, one {# σφάλμα} other {# σφάλματα}}, ανανεώστε τη σελίδα για να δείτε νέα στοιχεία μεταφόρτωσης.", + "upload_finished": "Ολοκλήρωση μεταφόρτωσης", "upload_progress": "Απομένουν {remaining, number} - Ολοκληρώθηκαν {processed, number}/{total, number}", "upload_skipped_duplicates": "Παραλείφθηκαν {count, plural, one {# διπλότυπο στοιχείο} other {# διπλότυπα στοιχεία}}", "upload_status_duplicates": "Διπλότυπα", @@ -1890,6 +1966,7 @@ "upload_success": "Η μεταφόρτωση ολοκληρώθηκε, ανανεώστε τη σελίδα για να δείτε τα νέα αντικείμενα.", "upload_to_immich": "Μεταφόρτωση στο Immich ({count})", "uploading": "Μεταφορτώνεται", + "uploading_media": "Μεταφόρτωση πολυμέσων", "url": "URL", "usage": "Χρήση", "use_biometric": "Χρήση βιομετρικών στοιχείων", @@ -1910,6 +1987,7 @@ "user_usage_stats_description": "Προβολή στατιστικών χρήσης λογαριασμού", "username": "Όνομα Χρήστη", "users": "Χρήστες", + "users_added_to_album_count": "Προστέθηκε {count, plural, one {# χρήστης} other {# χρήστες}} στο άλμπουμ", "utilities": "Βοηθητικά προγράμματα", "validate": "Επικύρωση", "validate_endpoint_error": "Παρακαλώ εισάγετε ένα έγκυρο URL", @@ -1928,6 +2006,7 @@ "view_album": "Προβολή Άλμπουμ", "view_all": "Προβολή Όλων", "view_all_users": "Προβολή όλων των χρηστών", + "view_details": "Προβολή Λεπτομερειών", "view_in_timeline": "Προβολή στο χρονοδιάγραμμα", "view_link": "Προβολή σύνδεσμου", "view_links": "Προβολή συνδέσμων", diff --git a/i18n/es.json b/i18n/es.json index 83d1c9d355..e78c6936c7 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -182,7 +182,7 @@ "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, según el uso actual", "no_paths_added": "No se han añadido carpetas", "no_pattern_added": "No se han añadido patrones", - "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", + "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente subido, lanza el", "note_cannot_be_changed_later": "NOTA: ¡No se puede cambiar posteriormente!", "notification_email_from_address": "Desde", "notification_email_from_address_description": "Dirección de correo electrónico del remitente, por ejemplo: \"Immich Photo Server \". Asegúrate de utilizar una dirección desde la que puedas enviar correos electrónicos.", @@ -263,7 +263,7 @@ "storage_template_onboarding_description_v2": "Al habilitar esta función, los archivos se organizarán automáticamente según la plantilla definida por el usuario. Para más información, consulte la documentación.", "storage_template_path_length": "Límite aproximado de la longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", + "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso subido", "storage_template_user_label": "{label} es la etiqueta de almacenamiento del usuario", "system_settings": "Ajustes del Sistema", "tag_cleanup_job": "Limpieza de etiquetas", @@ -397,6 +397,7 @@ "album_cover_updated": "Portada del álbum actualizada", "album_delete_confirmation": "¿Estás seguro de que deseas eliminar el álbum {album}?", "album_delete_confirmation_description": "Si este álbum se comparte, otros usuarios ya no podrán acceder a él.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUIDOS", "album_info_card_backup_album_included": "INCLUIDOS", "album_info_updated": "Información del álbum actualizada", @@ -406,6 +407,7 @@ "album_options": "Opciones del Album", "album_remove_user": "¿Eliminar usuario?", "album_remove_user_confirmation": "¿Estás seguro de que quieres eliminar a {user}?", + "album_search_not_found": "No se encontraron álbumes que coincidan con tu búsqueda", "album_share_no_users": "Parece que has compartido este álbum con todos los usuarios o no tienes ningún usuario con quien compartirlo.", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificación por correo electrónico cuando un álbum compartido tenga nuevos archivos", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Ordenación por defecto de los álbumes", "albums_default_sort_order_description": "Orden de clasificación inicial de los recursos al crear nuevos álbumes.", "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", + "albums_on_device_count": "Álbumes en el dispositivo ({count})", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -508,6 +511,7 @@ "back_close_deselect": "Atrás, cerrar o anular la selección", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para poder cambiar de red mientras se ejecuta en segundo plano, Immich debe tener *siempre* acceso a la ubicación precisa para que la aplicación pueda leer el nombre de la red Wi-Fi", + "backup": "Copia de Seguridad", "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir", "backup_album_selection_page_assets_scatter": "Los elementos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opciones de Copia de Seguridad", "backup_setting_subtitle": "Administra las configuraciones de respaldo en segundo y primer plano", "backward": "Retroceder", + "beta_sync": "Estado de Sincronización Beta", + "beta_sync_subtitle": "Administrar el nuevo sistema de sincronización", "biometric_auth_enabled": "Autentificación biométrica habilitada", "biometric_locked_out": "Estás bloqueado de la autentificación biométrica", "biometric_no_options": "Sin opciones biométricas disponibles", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Borrar caché", "cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.", "cache_settings_duplicated_assets_clear_button": "LIMPIAR", - "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos en la lista negra de la app", + "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos ignorados por la aplicación", "cache_settings_duplicated_assets_title": "Elementos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas de la biblioteca", "cache_settings_statistics_full": "Imágenes completas", @@ -605,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar búsqueda", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "No se pueden fusionar personas", "cannot_undo_this_action": "¡No puedes deshacer esta acción!", "cannot_update_the_description": "No se puede actualizar la descripción", @@ -718,6 +725,7 @@ "current_server_address": "Dirección actual del servidor", "custom_locale": "Configuración regional personalizada", "custom_locale_description": "Formatear fechas y números según el idioma y la región", + "custom_url": "URL personalizada", "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", @@ -737,13 +745,14 @@ "default_locale": "Configuración regional predeterminada", "default_locale_description": "Formatee fechas y números según la configuración regional de su navegador", "delete": "Eliminar", - "delete_action_prompt": "{count} eliminados permanentemente", + "delete_action_confirmation_message": "¿Está seguro que desea eliminar este archivo? Esta acción lo moverá a la papelera del servidor y le preguntará si desea eliminarlo localmente", + "delete_action_prompt": "{count} eliminados", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serán eliminados permanentemente de Immich y de tu dispositivo", - "delete_dialog_alert_local": "Estas imágenes van a ser borradas de tu dispositivo, pero seguirán disponibles en el servidor Immich", - "delete_dialog_alert_local_non_backed_up": "Algunas de las imágenes no tienen copia de seguridad y serán borradas de forma permanente de tu dispositivo", - "delete_dialog_alert_remote": "Estas imágenes van a ser borradas de forma permanente del servidor Immich", + "delete_dialog_alert_local": "Estos elementos se eliminarán permanente de tu dispositivo pero seguirán disponibles en el servidor de Immich", + "delete_dialog_alert_local_non_backed_up": "Algunos de los elementos no tienen copia de seguridad en Immich y serán borrados permanentemente de tu dispositivo", + "delete_dialog_alert_remote": "Estas imágenes van a ser borradas permanentemente del servidor de Immich", "delete_dialog_ok_force": "Borrar de todos modos", "delete_dialog_title": "Eliminar Permanentemente", "delete_duplicates_confirmation": "¿Está seguro de que desea eliminar permanentemente estos duplicados?", @@ -755,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar enlace compartido", "delete_shared_link_dialog_title": "Eliminar enlace compartido", "delete_tag": "Eliminar etiqueta", @@ -765,6 +776,7 @@ "description": "Descripción", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "deselect_all": "Deseleccionar Todo", "details": "Detalles", "direction": "Dirección", "disabled": "Deshabilitado", @@ -839,6 +851,7 @@ "empty_trash": "Vaciar papelera", "empty_trash_confirmation": "¿Estás seguro de que quieres vaciar la papelera? Esto eliminará permanentemente todos los archivos de la basura de Immich.\n¡No puedes deshacer esta acción!", "enable": "Habilitar", + "enable_backup": "Habilitar Copia de Seguridad", "enable_biometric_auth_description": "Introduce tu código PIN para habilitar la autentificación biométrica", "enabled": "Habilitado", "end_date": "Fecha final", @@ -995,6 +1008,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", + "export_database": "Exportar Base de Datos", + "export_database_description": "Exportar la Base de Datos SQLite", "extension": "Extensión", "external": "Externo", "external_libraries": "Bibliotecas Externas", @@ -1046,6 +1061,9 @@ "haptic_feedback_switch": "Activar respuesta háptica", "haptic_feedback_title": "Respuesta Háptica", "has_quota": "Su cuota", + "hash_asset": "Generar hash del archivo", + "hashed_assets": "Archivos con hash generado", + "hashing": "Generando hash", "header_settings_add_header_tip": "Añadir cabecera", "header_settings_field_validator_msg": "El valor no puede estar vacío", "header_settings_header_name_input": "Nombre de la cabecera", @@ -1078,6 +1096,7 @@ "host": "Host", "hour": "Hora", "id": "ID", + "idle": "Inactivo", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirán a Immich", "image": "Imagen", @@ -1135,6 +1154,7 @@ "language_no_results_title": "No se han encontrado idiomas", "language_search_hint": "Buscar idiomas...", "language_setting_description": "Selecciona tu idioma preferido", + "large_files": "Archivos Grandes", "last_seen": "Ultima vez visto", "latest_version": "Última versión", "latitude": "Latitud", @@ -1154,13 +1174,14 @@ "light": "Claro", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", - "link_options": "Opciones de enlace", "link_to_oauth": "Enlace a OAuth", "linked_oauth_account": "Cuenta OAuth vinculada", "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la búsqueda", + "local": "Local", "local_asset_cast_failed": "No es posible transmitir un recurso que no está subido al servidor", + "local_assets": "Archivos Locales", "local_network": "Red local", "local_network_sheet_info": "La aplicación se conectará al servidor a través de esta URL cuando utilice la red Wi-Fi especificada", "location_permission": "Permiso de ubicación", @@ -1217,8 +1238,7 @@ "manage_your_devices": "Administre sus dispositivos conectados", "manage_your_oauth_connection": "Administra tu conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "No se pudo obtener la posición del usuario", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Usar esta ubicación", @@ -1317,6 +1337,7 @@ "no_results": "Sin resultados", "no_results_description": "Pruebe con un sinónimo o una palabra clave más general", "no_shared_albums_message": "Crea un álbum para compartir fotos y vídeos con personas de tu red", + "no_uploads_in_progress": "No hay cargas en progreso", "not_in_any_album": "Sin álbum", "not_selected": "No seleccionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos subidos previamente, ejecute el", @@ -1354,6 +1375,7 @@ "original": "original", "other": "Otro", "other_devices": "Otro dispositivo", + "other_entities": "Otras entidades", "other_variables": "Otras variables", "owned": "Propio", "owner": "Propietario", @@ -1485,6 +1507,7 @@ "purchase_server_description_2": "Estado del soporte", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "La clave del producto del servidor la administra el administrador", + "queue_status": "Poniendo en cola {count}/{total}", "rating": "Valoración", "rating_clear": "Borrar calificación", "rating_count": "{count, plural, one {# estrella} other {# estrellas}}", @@ -1513,6 +1536,8 @@ "refreshing_faces": "Recargando caras", "refreshing_metadata": "Recargando metadatos", "regenerating_thumbnails": "Recargando miniaturas", + "remote": "Remoto", + "remote_assets": "Elementos remotos", "remove": "Eliminar", "remove_assets_album_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del álbum?", "remove_assets_shared_link_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del enlace compartido?", @@ -1550,19 +1575,25 @@ "reset_password": "Restablecer la contraseña", "reset_people_visibility": "Restablecer la visibilidad de las personas", "reset_pin_code": "Restablecer PIN", + "reset_sqlite": "Restablecer la Base de Datos SQLite", + "reset_sqlite_confirmation": "¿Estás seguro que deseas restablecer la base de datos SQLite? Deberás cerrar sesión y volver a iniciarla para resincronizar los datos", + "reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite", "reset_to_default": "Restablecer los valores predeterminados", "resolve_duplicates": "Resolver duplicados", "resolved_all_duplicates": "Todos los duplicados resueltos", "restore": "Restaurar", "restore_all": "Restaurar todo", + "restore_trash_action_prompt": "{count} restaurado de la papelera", "restore_user": "Restaurar usuario", "restored_asset": "Archivo restaurado", "resume": "Continuar", "retry_upload": "Reintentar subida", "review_duplicates": "Revisar duplicados", + "review_large_files": "Revisar archivos grandes", "role": "Rol", "role_editor": "Editor", "role_viewer": "Visor", + "running": "En ejecución", "save": "Guardar", "save_to_gallery": "Guardado en la galería", "saved_api_key": "Clave API guardada", @@ -1716,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copiado al portapapeles", "shared_link_clipboard_text": "Enlace: {link}\nContraseña: {password}", "shared_link_create_error": "Error creando el enlace compartido", + "shared_link_custom_url_description": "Accede a este enlace compartido con una URL personalizada", "shared_link_edit_description_hint": "Introduce la descripción del enlace", "shared_link_edit_expire_after_option_day": "1 día", "shared_link_edit_expire_after_option_days": "{count} días", @@ -1741,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrar enlaces compartidos", "shared_link_options": "Opciones de enlaces compartidos", + "shared_link_password_description": "Requerir una contraseña para acceder a este enlace compartido", "shared_links": "Enlaces compartidos", "shared_links_description": "Comparte fotos y vídeos con un enlace", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos y vídeos compartidos.}}", @@ -1816,6 +1849,7 @@ "storage_quota": "Cuota de Almacenamiento", "storage_usage": "{used} de {available} en uso", "submit": "Enviar", + "success": "Éxito", "suggestions": "Sugerencias", "sunrise_on_the_beach": "Amanecer en la playa", "support": "Soporte", @@ -1825,6 +1859,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los álbumes seleccionados a respaldar", + "sync_local": "Sincronización Local", + "sync_remote": "Sincronización Remota", "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los álbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", @@ -1835,6 +1871,7 @@ "tag_updated": "Etiqueta actualizada: {tag}", "tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}", "tags": "Etiquetas", + "tap_to_run_job": "Toca para ejecutar la tarea", "template": "Plantilla", "theme": "Tema", "theme_selection": "Selección de tema", @@ -1914,10 +1951,13 @@ "updated_at": "Actualizado", "updated_password": "Contraseña actualizada", "upload": "Subir", + "upload_action_prompt": "{count} en cola para carga", "upload_concurrency": "Subidas simultáneas", + "upload_details": "Cargar Detalles", "upload_dialog_info": "¿Quieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la página para ver los nuevos recursos de la subida.", + "upload_finished": "Carga finalizada", "upload_progress": "Restante {remaining, number} - Procesado {processed, number}/{total, number}", "upload_skipped_duplicates": "Saltado {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicados", @@ -1926,6 +1966,7 @@ "upload_success": "Subida realizada correctamente, actualice la página para ver los nuevos recursos de subida.", "upload_to_immich": "Subir a Immich ({count})", "uploading": "Subiendo", + "uploading_media": "Subiendo medios", "url": "URL", "usage": "Uso", "use_biometric": "Uso biométrico", @@ -1965,6 +2006,7 @@ "view_album": "Ver Álbum", "view_all": "Ver todas", "view_all_users": "Mostrar todos los usuarios", + "view_details": "Ver Detalles", "view_in_timeline": "Mostrar en la línea de tiempo", "view_link": "Ver enlace", "view_links": "Mostrar enlaces", diff --git a/i18n/et.json b/i18n/et.json index 1d293a3bdf..29bacae5c1 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -14,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_birthday": "Lisa sünnipäev", "add_endpoint": "Lisa lõpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", @@ -44,6 +45,13 @@ "backup_database": "Loo andmebaasi tõmmis", "backup_database_enable_description": "Luba andmebaasi tõmmised", "backup_keep_last_amount": "Eelmiste tõmmiste arv, mida alles hoida", + "backup_onboarding_1_description": "asukohaväline koopia pilves või teises füüsilises asukohas.", + "backup_onboarding_2_description": "lokaalset koopiat erinevatel seadmetel. See hõlmab põhifaile ja nende failide lokaalsed varundust.", + "backup_onboarding_3_description": "koopiat su andmetest, kaasa arvatud originaalfailid. See hõlmab üht asukohavälist ja kaht lokaalset koopiat.", + "backup_onboarding_description": "Andmete kaitsmiseks on soovituslik 3-2-1 varundusstrateegia. Põhjaliku varunduse jaoks peaksid talletama koopiaid nii oma üleslaaditud fotodest ja videotest kui ka Immich'i andmebaasist.", + "backup_onboarding_footer": "Rohkem informatsiooni Immich'i varundamise kohta leiad dokumentatsioonist.", + "backup_onboarding_parts_title": "3-2-1 varundus hõlmab:", + "backup_onboarding_title": "Varukoopiad", "backup_settings": "Andmebaasi tõmmiste seaded", "backup_settings_description": "Halda andmebaasi tõmmiste seadeid.", "cleared_jobs": "Tööted eemaldatud: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Albumi kaanepilt muudetud", "album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?", "album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.", + "album_deleted": "Album kustutatud", "album_info_card_backup_album_excluded": "VÄLJA JÄETUD", "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", @@ -510,6 +519,7 @@ "back_close_deselect": "Tagasi, sulge või tühista valik", "background_location_permission": "Taustal asukoha luba", "background_location_permission_content": "Et taustal töötades võrguühendust vahetada, peab Immich'il *alati* olema täpse asukoha luba, et rakendus saaks WiFi-võrgu nime lugeda", + "backup": "Varundamine", "backup_album_selection_page_albums_device": "Albumid seadmel ({count})", "backup_album_selection_page_albums_tap": "Puuduta kaasamiseks, topeltpuuduta välistamiseks", "backup_album_selection_page_assets_scatter": "Üksused võivad olla jaotatud mitme albumi vahel. Seega saab albumeid varundamise protsessi kaasata või välistada.", @@ -723,6 +733,7 @@ "current_server_address": "Praegune serveri aadress", "custom_locale": "Kohandatud lokaat", "custom_locale_description": "Vorminda kuupäevad ja arvud vastavalt keelele ja regioonile", + "custom_url": "Kohandatud URL", "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", @@ -742,7 +753,8 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", - "delete_action_prompt": "{count} jäädavalt kustutatud", + "delete_action_confirmation_message": "Kas oled kindel, et soovid selle üksuse kustutada? See toiming liigutab üksuse serveri prügikasti ja küsib, kas soovid selle lokaalselt kustutada", + "delete_action_prompt": "{count} kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?", "delete_dialog_alert": "Need üksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", + "delete_permanently": "Kustuta jäädavalt", + "delete_permanently_action_prompt": "{count} jäädavalt kustutatud", "delete_shared_link": "Kustuta jagatud link", "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", @@ -815,6 +829,7 @@ "edit": "Muuda", "edit_album": "Muuda albumit", "edit_avatar": "Muuda avatari", + "edit_birthday": "Muuda sünnipäeva", "edit_date": "Muuda kuupäeva", "edit_date_and_time": "Muuda kuupäeva ja kellaaega", "edit_description": "Muuda kirjeldust", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_description_error": "Viga kirjelduse muutmisel", "exif_bottom_sheet_details": "ÜKSIKASJAD", "exif_bottom_sheet_location": "ASUKOHT", "exif_bottom_sheet_people": "ISIKUD", @@ -1002,6 +1018,8 @@ "explorer": "Brauser", "export": "Ekspordi", "export_as_json": "Ekspordi JSON-formaati", + "export_database": "Ekspordi andmebaas", + "export_database_description": "Ekspordi SQLite andmebaas", "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Ühtegi keelt ei leitud", "language_search_hint": "Otsi keeli...", "language_setting_description": "Vali oma eelistatud keel", + "large_files": "Suured failid", "last_seen": "Viimati nähtud", "latest_version": "Uusim versioon", "latitude": "Laiuskraad", @@ -1165,13 +1184,12 @@ "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", - "link_options": "Lingi valikud", "link_to_oauth": "Ühenda OAuth", "linked_oauth_account": "OAuth konto ühendatud", "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", - "local": "Lokaalne üksus", + "local": "Lokaalsed", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Halda oma autenditud seadmeid", "manage_your_oauth_connection": "Halda oma OAuth ühendust", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotot", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotot}}", "map_cannot_get_user_location": "Ei saa kasutaja asukohta tuvastada", "map_location_dialog_yes": "Jah", "map_location_picker_page_use_location": "Kasuta seda asukohta", @@ -1529,7 +1546,7 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", - "remote": "Kaugüksus", + "remote": "Serveris", "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", @@ -1582,6 +1599,7 @@ "resume": "Jätka", "retry_upload": "Proovi üleslaadimist uuesti", "review_duplicates": "Vaata duplikaadid läbi", + "review_large_files": "Vaata suured failid läbi", "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopeeritud lõikelauale", "shared_link_clipboard_text": "Link: {link}\nParool: {password}", "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_custom_url_description": "Ligipääs jagatud lingile kohandatud URL-i kaudu", "shared_link_edit_description_hint": "Sisesta jagatud lingi kirjeldus", "shared_link_edit_expire_after_option_day": "1 päev", "shared_link_edit_expire_after_option_days": "{count} päeva", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", + "shared_link_password_description": "Nõua jagatud lingile ligi pääsemiseks parooli", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi üles", + "upload_action_prompt": "{count} üleslaadimise ootel", "upload_concurrency": "Üleslaadimise samaaegsus", "upload_details": "Üleslaadimise üksikasjad", "upload_dialog_info": "Kas soovid valitud üksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse üleslaadimine", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", + "upload_finished": "Üleslaadimine lõpetatud", "upload_progress": "Ootel {remaining, number} - Töödeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud üksus} other {# dubleeritud üksust}} vahele jäetud", "upload_status_duplicates": "Duplikaadid", @@ -1954,6 +1976,7 @@ "upload_success": "Üleslaadimine õnnestus, uute üksuste nägemiseks värskenda lehte.", "upload_to_immich": "Laadi Immich'isse ({count})", "uploading": "Üleslaadimine", + "uploading_media": "Meediumi üleslaadimine", "url": "URL", "usage": "Kasutus", "use_biometric": "Kasuta biomeetriat", diff --git a/i18n/fa.json b/i18n/fa.json index 84857479b3..5b463655b2 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -471,7 +471,6 @@ "library": "کتابخانه", "library_options": "گزینه‌های کتابخانه", "light": "روشن", - "link_options": "گزینه‌های لینک", "link_to_oauth": "اتصال به OAuth", "linked_oauth_account": "حساب OAuth متصل شده", "list": "لیست", @@ -493,7 +492,6 @@ "manage_your_devices": "مدیریت دستگاه‌های متصل", "manage_your_oauth_connection": "مدیریت اتصال OAuth", "map": "نقشه", - "map_assets_in_bound": "{count} عکس", "map_assets_in_bounds": "{count} عکس ها", "map_cannot_get_user_location": "موقعیت مکانی در دسترس نیست", "map_location_dialog_yes": "بله", diff --git a/i18n/fi.json b/i18n/fi.json index 4e12253cf2..73ce337de0 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -373,6 +373,7 @@ "admin_password": "Ylläpitäjän salasana", "administration": "Ylläpito", "advanced": "Edistyneet", + "advanced_settings_beta_timeline_subtitle": "Kokeile uutta sovelluskokemusta", "advanced_settings_enable_alternate_media_filter_subtitle": "Käytä tätä vaihtoehtoa suodattaaksesi mediaa synkronoinnin aikana vaihtoehtoisten kriteerien perusteella. Kokeile tätä vain, jos sovelluksessa on ongelmia kaikkien albumien tunnistamisessa.", "advanced_settings_enable_alternate_media_filter_title": "[KOKEELLINEN] Käytä vaihtoehtoisen laitteen albumin synkronointisuodatinta", "advanced_settings_log_level_title": "Kirjaustaso: {level}", @@ -506,6 +507,7 @@ "back_close_deselect": "Palaa, sulje tai poista valinnat", "background_location_permission": "Taustasijainnin käyttöoikeus", "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "backup": "Varmuuskopiointi", "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", "backup_album_selection_page_assets_scatter": "Kohteet voivat olla hajaantuneina useisiin albumeihin. Albumeita voidaan sisällyttää varmuuskopiointiin tai jättää siitä pois.", @@ -1149,7 +1151,6 @@ "light": "Vaalea", "like_deleted": "Tykkäys poistettu", "link_motion_video": "Linkitä liikevideo", - "link_options": "Linkin asetukset", "link_to_oauth": "Linkki OAuth", "linked_oauth_account": "Linkitetty OAuth-tili", "list": "Lista", @@ -1212,7 +1213,6 @@ "manage_your_devices": "Hallitse sisäänkirjautuneita laitteitasi", "manage_your_oauth_connection": "Hallitse OAuth-yhteyttäsi", "map": "Kartta", - "map_assets_in_bound": "{count} kuva", "map_assets_in_bounds": "{count} kuvaa", "map_cannot_get_user_location": "Käyttäjän sijaintia ei voitu määrittää", "map_location_dialog_yes": "Kyllä", diff --git a/i18n/fr.json b/i18n/fr.json index ff9f783771..872c2e4984 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -397,6 +397,7 @@ "album_cover_updated": "Couverture de l'album mise à jour", "album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer l'album {album} ?", "album_delete_confirmation_description": "Si cet album est partagé, les autres utilisateurs ne pourront plus y accéder.", + "album_deleted": "Album supprimé", "album_info_card_backup_album_excluded": "EXCLUS", "album_info_card_backup_album_included": "INCLUS", "album_info_updated": "Détails de l'album mis à jour", @@ -510,6 +511,7 @@ "back_close_deselect": "Retournez en arrière, fermez ou désélectionnez", "background_location_permission": "Permission de localisation en arrière plan", "background_location_permission_content": "Afin de pouvoir changer d'adresse en arrière plan, Immich doit avoir *en permanence* accès à la localisation précise, afin d'accéder au le nom du réseau Wi-Fi utilisé", + "backup": "Sauvegarde", "backup_album_selection_page_albums_device": "Albums sur l'appareil ({count})", "backup_album_selection_page_albums_tap": "Tapez pour inclure, tapez deux fois pour exclure", "backup_album_selection_page_assets_scatter": "Les éléments peuvent être répartis sur plusieurs albums. De ce fait, les albums peuvent être inclus ou exclus pendant le processus de sauvegarde.", @@ -562,7 +564,7 @@ "backup_controller_page_to_backup": "Albums à sauvegarder", "backup_controller_page_total_sub": "Toutes les photos et vidéos uniques des albums sélectionnés", "backup_controller_page_turn_off": "Désactiver la sauvegarde", - "backup_controller_page_turn_on": "Activer la sauvegarde", + "backup_controller_page_turn_on": "Activer la sauvegarde au premier plan", "backup_controller_page_uploading_file_info": "Envoi des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", "backup_info_card_assets": "éléments", @@ -592,7 +594,7 @@ "cache_settings_clear_cache_button": "Effacer le cache", "cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.", "cache_settings_duplicated_assets_clear_button": "EFFACER", - "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont exclues par l'application", + "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont ignorées par l'application", "cache_settings_duplicated_assets_title": "Médias dupliqués ({count})", "cache_settings_statistics_album": "Miniatures de la bibliothèque", "cache_settings_statistics_full": "Images complètes", @@ -723,6 +725,7 @@ "current_server_address": "Adresse actuelle du serveur", "custom_locale": "Paramètres régionaux personnalisés", "custom_locale_description": "Afficher les dates et nombres en fonction des paramètres régionaux", + "custom_url": "URL personnalisée", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", @@ -742,7 +745,8 @@ "default_locale": "Région par défaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", - "delete_action_prompt": "{count} supprimé(s) définitivement", + "delete_action_confirmation_message": "Êtes-vous sûr de vouloir supprimer ce média ? Cela déplacera le média dans la poubelle du serveur et vous demandera si vous voulez le supprimer localement", + "delete_action_prompt": "{count} supprimé(s)", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clé API ?", "delete_dialog_alert": "Ces médias seront définitivement supprimés de Immich et de votre appareil", @@ -760,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Suppression des données sauvegardées uniquement", "delete_local_dialog_ok_force": "Supprimer tout de même", "delete_others": "Supprimer les autres", + "delete_permanently": "Supprimer définitivement", + "delete_permanently_action_prompt": "{count} supprimé(s) définitivement", "delete_shared_link": "Supprimer le lien partagé", "delete_shared_link_dialog_title": "Supprimer le lien partagé", "delete_tag": "Supprimer l'étiquette", @@ -845,11 +851,11 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", - "enable_backup": "Activer Backup", + "enable_backup": "Activer la sauvegarde", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", - "enqueued": "Mis en file", + "enqueued": "Mis en file d'attente", "enter_wifi_name": "Entrez le nom du réseau wifi", "enter_your_pin_code": "Entrez votre code PIN", "enter_your_pin_code_subtitle": "Entrez votre code PIN pour accéder au dossier verrouillé", @@ -1002,6 +1008,8 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", + "export_database": "Exporter la base de données", + "export_database_description": "Exporter la base de données SQLite", "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", @@ -1088,6 +1096,7 @@ "host": "Hôte", "hour": "Heure", "id": "ID", + "idle": "Inactif", "ignore_icloud_photos": "Ignorer les photos iCloud", "ignore_icloud_photos_description": "Les photos stockées sur iCloud ne seront pas envoyées sur le serveur Immich", "image": "Image", @@ -1145,6 +1154,7 @@ "language_no_results_title": "Aucune langue trouvée", "language_search_hint": "Recherche de langues...", "language_setting_description": "Sélectionnez votre langue préférée", + "large_files": "Fichiers volumineux", "last_seen": "Dernièrement utilisé", "latest_version": "Dernière version", "latitude": "Latitude", @@ -1164,7 +1174,6 @@ "light": "Clair", "like_deleted": "Réaction « j'aime » supprimée", "link_motion_video": "Lier la photo animée", - "link_options": "Options de lien", "link_to_oauth": "Lien au service OAuth", "linked_oauth_account": "Compte OAuth rattaché", "list": "Liste", @@ -1229,8 +1238,7 @@ "manage_your_devices": "Gérer vos appareils", "manage_your_oauth_connection": "Gérer votre connexion OAuth", "map": "Carte", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Impossible d'obtenir la localisation de l'utilisateur", "map_location_dialog_yes": "Oui", "map_location_picker_page_use_location": "Utiliser ma position", @@ -1499,7 +1507,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", - "queue_status": "File d'attente {count}/{total}", + "queue_status": "{count}/{total} en file d'attente", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1528,7 +1536,7 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", - "remote": "A distance", + "remote": "À distance", "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", @@ -1568,7 +1576,7 @@ "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", "reset_sqlite": "Réinitialiser la base de données SQLite", - "reset_sqlite_confirmation": "Êtes vous sur que vous voulez réinitialiser la base de données SQLite ? Vous devrez vous déconnecter and vous reconnecter à nouveau pour re-synchroniser les données", + "reset_sqlite_confirmation": "Êtes-vous certain de vouloir réinitialiser la base de données SQLite ? Vous devrez vous déconnecter puis vous reconnecter à nouveau pour resynchroniser les données", "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", @@ -1581,10 +1589,11 @@ "resume": "Reprendre", "retry_upload": "Réessayer l'envoi", "review_duplicates": "Consulter les doublons", + "review_large_files": "Consulter les fichiers volumineux", "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", - "running": "En marche", + "running": "En cours", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1738,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copié dans le presse-papier", "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", "shared_link_create_error": "Erreur pendant la création du lien partagé", + "shared_link_custom_url_description": "Accéder à ce lien partagé avec une URL personnalisée", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", "shared_link_edit_expire_after_option_days": "{count} jours", @@ -1763,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gérer les liens partagés", "shared_link_options": "Options de lien partagé", + "shared_link_password_description": "Demander un mot de passe pour accéder à ce lien partagé", "shared_links": "Liens partagés", "shared_links_description": "Partager les photos et vidéos via un lien", "shared_photos_and_videos_count": "{assetCount, plural, other {# photos et vidéos partagées.}}", @@ -1940,11 +1951,13 @@ "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", + "upload_action_prompt": "{count} en attente d'envoi", "upload_concurrency": "Envois simultanés", - "upload_details": "Uploader les details", + "upload_details": "Détails des envois", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", + "upload_finished": "Envoi fini", "upload_progress": "{remaining, number} restant(s) - {processed, number} traité(s)/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# doublon ignoré} other {# doublons ignorés}}", "upload_status_duplicates": "Doublons", @@ -1953,6 +1966,7 @@ "upload_success": "Envoi réussi. Rafraîchissez la page pour voir les nouveaux médias envoyés.", "upload_to_immich": "Envoyer vers Immich ({count})", "uploading": "Envoi", + "uploading_media": "Envoi du média", "url": "URL", "usage": "Utilisation", "use_biometric": "Utiliser l'authentification biométrique", diff --git a/i18n/gl.json b/i18n/gl.json index 70146abb53..92b3c8fb13 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -476,6 +476,7 @@ "back_close_deselect": "Atrás, pechar ou deseleccionar", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso á ubicación precisa para que a aplicación poida ler o nome da rede wifi", + "backup": "Copia de Seguridade", "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios álbums. Polo tanto, os álbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", @@ -1070,7 +1071,6 @@ "light": "Claro", "like_deleted": "Gústame eliminado", "link_motion_video": "Ligar vídeo en movemento", - "link_options": "Opcións da ligazón", "link_to_oauth": "Ligar a OAuth", "linked_oauth_account": "Conta OAuth ligada", "list": "Lista", @@ -1129,7 +1129,6 @@ "manage_your_devices": "Xestionar os teus dispositivos con sesión iniciada", "manage_your_oauth_connection": "Xestionar a túa conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "Non se pode obter a ubicación do usuario", "map_location_dialog_yes": "Si", diff --git a/i18n/he.json b/i18n/he.json index 49973dbc63..3139c2cde6 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -14,6 +14,7 @@ "add_a_location": "הוספת מיקום", "add_a_name": "הוספת שם", "add_a_title": "הוספת כותרת", + "add_birthday": "הוספת יום הולדת", "add_endpoint": "הוסף נקודת קצה", "add_exclusion_pattern": "הוספת דפוס החרגה", "add_import_path": "הוספת נתיב יבוא", @@ -44,6 +45,13 @@ "backup_database": "גיבוי מסד נתונים", "backup_database_enable_description": "אפשר גיבויי מסד נתונים", "backup_keep_last_amount": "כמות של גיבויים קודמים שיש לשמור", + "backup_onboarding_1_description": "העתק בענן או במיקום פיזי אחר מחוץ למקום השרת.", + "backup_onboarding_2_description": "העתקים מקומיים במכשירים שונים. זה כולל את הקבצים הראשיים וגיבוי של הקבצים האלה באופן מקומי.", + "backup_onboarding_3_description": "סך כל ההעתקים של הנתונים שלך, כולל הקבצים המקוריים. זה כולל העתק אחד מחוץ למקום השרת ושני העתקים מקומיים.", + "backup_onboarding_description": "אסטרטגיית גיבוי 3-2-1 הינה מומלצת על מנת להגן על הנתונים שלך. עליך להשאיר העתקים של תמונות/סרטונים שהועלו כמו גם את מסד הנתונים של Immich עבור פתרון גיבוי מקיף.", + "backup_onboarding_footer": "עבור מידע נוסף על גיבוי Immich, נא לפנות אל התיעוד.", + "backup_onboarding_parts_title": "גיבוי 3-2-1 כולל:", + "backup_onboarding_title": "גיבויים", "backup_settings": "הגדרות גיבוי", "backup_settings_description": "ניהול הגדרות גיבוי מסד נתונים.", "cleared_jobs": "נוקו משימות עבור: {job}", @@ -156,15 +164,15 @@ "map_settings": "מפה", "map_settings_description": "ניהול הגדרות מפה", "map_style_description": "כתובת אתר לערכת נושא של מפה style.json", - "memory_cleanup_job": "ניקוי זיכרון", - "memory_generate_job": "יצירת זיכרון", + "memory_cleanup_job": "ניקוי זיכרון (היום לפני..)", + "memory_generate_job": "יצירת זיכרון (היום לפני..)", "metadata_extraction_job": "חלץ מטא-נתונים", "metadata_extraction_job_description": "חלץ מטא-נתונים מכל תמונה, כגון GPS, פנים ורזולוציה", "metadata_faces_import_setting": "אפשר יבוא פנים", "metadata_faces_import_setting_description": "יבא פנים מנתוני EXIF של תמונה ומקבצים נלווים", "metadata_settings": "הגדרות מטא-נתונים", - "metadata_settings_description": "ניהול הגדרות מטא-נתונים", - "migration_job": "העברה", + "metadata_settings_description": "ניהול הגדרות metadata", + "migration_job": "נדידה", "migration_job_description": "העבר תמונות ממוזערות של תמונות ופנים למבנה התיקיות העדכני ביותר", "nightly_tasks_cluster_faces_setting_description": "בצע זיהוי פנים עבור פרצופים שזוהו לאחרונה", "nightly_tasks_cluster_new_faces_setting": "קבץ פנים חדשות", @@ -178,7 +186,7 @@ "nightly_tasks_settings_description": "נהל משימות ליליות", "nightly_tasks_start_time_setting": "זמן התחלה", "nightly_tasks_start_time_setting_description": "השעה שבה השרת מתחיל להריץ את המשימות הליליות", - "nightly_tasks_sync_quota_usage_setting": "סנכרן את השימוש באחסון", + "nightly_tasks_sync_quota_usage_setting": "סנכרון מכסת שימוש", "nightly_tasks_sync_quota_usage_setting_description": "עדכן את מכסת האחסון של המשתמש בהתאם לשימוש הנוכחי", "no_paths_added": "לא נוספו נתיבים", "no_pattern_added": "לא נוספה תבנית", @@ -210,6 +218,8 @@ "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "עקיפת URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override_description": "אפשר כאשר ספק OAuth לא מאפשר כתובת URI לנייד, כמו ''{callback}''", + "oauth_role_claim": "דרישת תפקיד", + "oauth_role_claim_description": "הענק גישת מנהל באופן אוטומטי אם תביעה זו קיימת. ערך התביעה יכול להיות 'user' או 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרות התחברות עם OAuth", "oauth_settings_more_details": "למידע נוסף אודות תכונה זו, בדוק את התיעוד.", @@ -371,10 +381,12 @@ "admin_password": "סיסמת מנהל", "administration": "ניהול", "advanced": "מתקדם", + "advanced_settings_beta_timeline_subtitle": "נסה את חווית האפליקציה החדשה", + "advanced_settings_beta_timeline_title": "ציר זמן (בטא)", "advanced_settings_enable_alternate_media_filter_subtitle": "השתמש באפשרות זו כדי לסנן מדיה במהלך הסנכרון לפי קריטריונים חלופיים. מומלץ להשתמש בזה רק אם יש בעיה בזיהוי כל האלבומים באפליקציה.", "advanced_settings_enable_alternate_media_filter_title": "[ניסיוני] השתמש במסנן סנכרון אלבום חלופי שמבכשיר", "advanced_settings_log_level_title": "רמת רישום ביומן: {level}", - "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לטעינה של תמונות ממוזערות מתמונות שבמכשיר. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום.", + "advanced_settings_prefer_remote_subtitle": "במכשירים מסוימים טעינת תמונות ממוזערות מקבצים מקומיים עלולה להיות איטית במיוחד. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום זאת.", "advanced_settings_prefer_remote_title": "העדף תמונות מרוחקות", "advanced_settings_proxy_headers_subtitle": "הגדר proxy headers שהיישום צריך לשלוח עם כל בקשת רשת", "advanced_settings_proxy_headers_title": "כותרות פרוקסי", @@ -393,6 +405,7 @@ "album_cover_updated": "עטיפת האלבום עודכנה", "album_delete_confirmation": "האם באמת ברצונך למחוק את האלבום {album}?", "album_delete_confirmation_description": "אם האלבום הזה משותף, משתמשים אחרים לא יוכלו לגשת אליו יותר.", + "album_deleted": "אלבום נמחק", "album_info_card_backup_album_excluded": "הוחרגו", "album_info_card_backup_album_included": "נכללו", "album_info_updated": "מידע האלבום עודכן", @@ -402,6 +415,7 @@ "album_options": "אפשרויות האלבום", "album_remove_user": "להסיר משתמש?", "album_remove_user_confirmation": "האם באמת ברצונך להסיר את {user}?", + "album_search_not_found": "לא נמצאו אלבומים התואמים לחיפוש שלך", "album_share_no_users": "נראה ששיתפת את האלבום הזה עם כל המשתמשים או שאין לך אף משתמש לשתף איתו.", "album_updated": "אלבום עודכן", "album_updated_setting_description": "קבל הודעת דוא\"ל כאשר לאלבום משותף יש תמונות חדשות", @@ -421,6 +435,7 @@ "albums_default_sort_order": "סדר מיון אלבומים ברירת מחדל", "albums_default_sort_order_description": "סדר מיון תמונות ראשוני בעת יצירת אלבומים חדשים.", "albums_feature_description": "אוספים של תמונות אשר ניתנים לשיתוף עם משתמשים אחרים.", + "albums_on_device_count": "אלבומים במכשיר ({count})", "all": "הכל", "all_albums": "כל האלבומים", "all_people": "כל האנשים", @@ -504,6 +519,7 @@ "back_close_deselect": "חזור, סגור, או בטל בחירה", "background_location_permission": "הרשאת מיקום ברקע", "background_location_permission_content": "כדי להחליף רשתות בעת ריצה ברקע, היישום צריך *תמיד* גישה למיקום מדויק על מנת לקרוא את השם של רשת האינטרנט האלחוטי", + "backup": "גיבוי", "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פעמיים כדי להחריג", "backup_album_selection_page_assets_scatter": "תמונות יכולות להתפזר על פני אלבומים מרובים. לפיכך, ניתן לכלול או להחריג אלבומים במהלך תהליך הגיבוי.", @@ -548,11 +564,11 @@ "backup_controller_page_none_selected": "אין בחירה", "backup_controller_page_remainder": "בהמתנה לגיבוי", "backup_controller_page_remainder_sub": "תמונות וסרטונים הנותרים לגיבוי מתוך בחירה", - "backup_controller_page_server_storage": "אחסון שרת", + "backup_controller_page_server_storage": "אחסון בשרת", "backup_controller_page_start_backup": "התחל גיבוי", "backup_controller_page_status_off": "גיבוי חזית אוטומטי כבוי", "backup_controller_page_status_on": "גיבוי חזית אוטומטי מופעל", - "backup_controller_page_storage_format": "{total} מתוך {used} בשימוש", + "backup_controller_page_storage_format": "{used}מתוך {total} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבות", "backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזית", @@ -567,6 +583,8 @@ "backup_options_page_title": "אפשרויות גיבוי", "backup_setting_subtitle": "ניהול הגדרות העלאת רקע וחזית", "backward": "אחורה", + "beta_sync": "סטטוס סנכרון (בטא)", + "beta_sync_subtitle": "נהל את מערכת הסנכרון החדשה", "biometric_auth_enabled": "אימות ביומטרי הופעל", "biometric_locked_out": "גישה לאימות הביומטרי נחסמה", "biometric_no_options": "אין אפשרויות זמינות עבור אימות ביומטרי", @@ -584,7 +602,7 @@ "cache_settings_clear_cache_button": "ניקוי מטמון", "cache_settings_clear_cache_button_title": "מנקה את המטמון של היישום. זה ישפיע באופן משמעותי על הביצועים של היישום עד שהמטמון מתמלא מחדש.", "cache_settings_duplicated_assets_clear_button": "נקה", - "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימה השחורה של היישום", + "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימת ההתעלמות של האפליקציה", "cache_settings_duplicated_assets_title": "({count}) תמונות משוכפלות", "cache_settings_statistics_album": "תמונות ממוזערות של ספרייה", "cache_settings_statistics_full": "תמונות מלאות", @@ -601,6 +619,7 @@ "cancel": "ביטול", "cancel_search": "ביטול חיפוש", "canceled": "בוטל", + "canceling": "מבטל", "cannot_merge_people": "לא ניתן למזג אנשים", "cannot_undo_this_action": "אין באפשרותך לבטל את הפעולה הזו!", "cannot_update_the_description": "לא ניתן לעדכן את התיאור", @@ -714,6 +733,7 @@ "current_server_address": "כתובת שרת נוכחית", "custom_locale": "אזור שפה מותאם אישית", "custom_locale_description": "עצב תאריכים ומספרים על סמך השפה והאזור", + "custom_url": "קישור מותאם אישית", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", @@ -733,7 +753,8 @@ "default_locale": "שפת ברירת מחדל", "default_locale_description": "פורמט תאריכים ומספרים מבוסס שפת הדפדפן שלך", "delete": "מחק", - "delete_action_prompt": "{count} נמחקו לצמיתות", + "delete_action_confirmation_message": "האם אתה בטוח שברצונך למחוק את התמונה הזאת? פעולה זו תעביר אותו לאשפה של השרת, ותשאל אם ברצונך למחוק אותו גם מהמכשיר המקומי", + "delete_action_prompt": "{count} נמחקו", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו לצמיתות מהשרת ומהמכשיר שלך", @@ -747,9 +768,12 @@ "delete_key": "מחק מפתח", "delete_library": "מחק ספרייה", "delete_link": "מחק קישור", + "delete_local_action_prompt": "{count} נמחקו באופן מקומי", "delete_local_dialog_ok_backed_up_only": "מחק את מה שמגובה בלבד", "delete_local_dialog_ok_force": "מחק בכל זאת", "delete_others": "מחק אחרים", + "delete_permanently": "מחק לצמיתות", + "delete_permanently_action_prompt": "{count} נמחקו לצמיתות", "delete_shared_link": "מחק קישור משותף", "delete_shared_link_dialog_title": "מחק קישור משותף", "delete_tag": "מחק תג", @@ -760,6 +784,7 @@ "description": "תיאור", "description_input_hint_text": "הוסף תיאור...", "description_input_submit_error": "שגיאה בעדכון תיאור, בדוק את היומן לפרטים נוספים", + "deselect_all": "בטל הכל", "details": "פרטים", "direction": "כיוון", "disabled": "מושבת", @@ -777,6 +802,7 @@ "documentation": "תיעוד", "done": "סיום", "download": "הורדה", + "download_action_prompt": "מוריד {count} תמונות", "download_canceled": "הורדה בוטלה", "download_complete": "הורדה הושלמה", "download_enqueue": "הורדה נוספה לתור", @@ -803,6 +829,7 @@ "edit": "ערוך", "edit_album": "ערוך אלבום", "edit_avatar": "ערוך תמונת פרופיל", + "edit_birthday": "עריכת יום הולדת", "edit_date": "ערוך תאריך", "edit_date_and_time": "ערוך תאריך ושעה", "edit_description": "ערוך תיאור", @@ -833,6 +860,7 @@ "empty_trash": "רוקן אשפה", "empty_trash_confirmation": "האם באמת ברצונך לרוקן את האשפה? זה יסיר לצמיתות את כל התמונות מהאשפה של השרת.\nאין באפשרותך לבטל פעולה זו!", "enable": "אפשר", + "enable_backup": "הפעל גיבוי", "enable_biometric_auth_description": "הזן את קוד ה־PIN שלך כדי להפעיל אימות ביומטרי", "enabled": "מופעל", "end_date": "תאריך סיום", @@ -969,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "הוסף תיאור...", + "exif_bottom_sheet_description_error": "שגיאה בעדכון התיאור", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", "exif_bottom_sheet_people": "אנשים", @@ -989,6 +1018,8 @@ "explorer": "סייר", "export": "ייצוא", "export_as_json": "ייצוא כ-JSON", + "export_database": "ייצא מסד נתונים", + "export_database_description": "ייצא מסד נתונים SQL", "extension": "סיומת", "external": "חיצוני", "external_libraries": "ספריות חיצוניות", @@ -1040,6 +1071,9 @@ "haptic_feedback_switch": "אפשר משוב ברטט", "haptic_feedback_title": "משוב ברטט", "has_quota": "יש מכסה", + "hash_asset": "גיבוב תמונה", + "hashed_assets": "תמונות מגובבות", + "hashing": "מגבב", "header_settings_add_header_tip": "הוסף כותרת", "header_settings_field_validator_msg": "ערך אינו יכול להיות ריק", "header_settings_header_name_input": "שם כותרת", @@ -1072,6 +1106,7 @@ "host": "מארח", "hour": "שעה", "id": "מזהה", + "idle": "ממתין", "ignore_icloud_photos": "התעלם מתמונות iCloud", "ignore_icloud_photos_description": "תמונות שמאוחסנות ב-iCloud לא יועלו לשרת", "image": "תמונה", @@ -1129,6 +1164,7 @@ "language_no_results_title": "לא נמצאה שפה", "language_search_hint": "חפש שפות...", "language_setting_description": "בחר את השפה המועדפת עליך", + "large_files": "קבצים גדולים", "last_seen": "נראה לאחרונה", "latest_version": "גרסה עדכנית ביותר", "latitude": "קו רוחב", @@ -1144,16 +1180,18 @@ "library_page_sort_created": "תאריך יצירה", "library_page_sort_last_modified": "שונה לאחרונה", "library_page_sort_title": "כותרת אלבום", + "licenses": "רישיונות", "light": "בהיר", "like_deleted": "לייק נמחק", "link_motion_video": "קשר סרטון תנועה", - "link_options": "אפשרויות קישור", "link_to_oauth": "קישור ל-OAuth", "linked_oauth_account": "חשבון OAuth מקושר", "list": "רשימה", "loading": "טוען", "loading_search_results_failed": "טעינת תוצאות החיפוש נכשלה", + "local": "מקומי", "local_asset_cast_failed": "לא ניתן לשדר תמונה שלא הועלתה לשרת", + "local_assets": "תמונות מקומיות", "local_network": "רשת מקומית", "local_network_sheet_info": "היישום יתחבר לשרת דרך הכתובת הזאת כאשר משתמשים ברשת האינטרנט האלחוטי שמצוינת", "location_permission": "הרשאת מיקום", @@ -1210,8 +1248,7 @@ "manage_your_devices": "ניהול המכשירים המחוברים שלך", "manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך", "map": "מפה", - "map_assets_in_bound": "תמונה {count}", - "map_assets_in_bounds": "{count} תמונות", + "map_assets_in_bounds": "{count, plural, one {תמונה #} other {# תמונות}}", "map_cannot_get_user_location": "לא ניתן לקבוע את מיקום המשתמש", "map_location_dialog_yes": "כן", "map_location_picker_page_use_location": "השתמש במיקום הזה", @@ -1245,7 +1282,7 @@ "memories_setting_description": "נהל את מה שרואים בזכרונות שלך", "memories_start_over": "התחל מחדש", "memories_swipe_to_close": "החלק למעלה כדי לסגור", - "memory": "זיכרון", + "memory": "זיכרון (היום לפני..)", "memory_lane_title": "משעול הזיכרונות {title}", "menu": "תפריט", "merge": "מזג", @@ -1310,6 +1347,7 @@ "no_results": "אין תוצאות", "no_results_description": "נסה להשתמש במילה נרדפת או במילת מפתח יותר כללית", "no_shared_albums_message": "צור אלבום כדי לשתף תמונות וסרטונים עם אנשים ברשת שלך", + "no_uploads_in_progress": "אין העלאות בתהליך", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", "note_apply_storage_label_to_previously_uploaded assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -1347,6 +1385,7 @@ "original": "מקורי", "other": "אחר", "other_devices": "מכשירים אחרים", + "other_entities": "ישויות אחרות", "other_variables": "משתנים אחרים", "owned": "בבעלות", "owner": "בעלים", @@ -1478,6 +1517,7 @@ "purchase_server_description_2": "מעמד תומך", "purchase_server_title": "שרת", "purchase_settings_server_activated": "מפתח המוצר של השרת מנוהל על ידי מנהל המערכת", + "queue_status": "בתור {count}/{total}", "rating": "דירוג כוכב", "rating_clear": "נקה דירוג", "rating_count": "{count, plural, one {כוכב #} other {# כוכבים}}", @@ -1506,6 +1546,8 @@ "refreshing_faces": "מרענן פרצופים", "refreshing_metadata": "מרענן מטא-נתונים", "regenerating_thumbnails": "מחדש תמונות ממוזערות", + "remote": "מרוחק", + "remote_assets": "תמונות מרוחקות", "remove": "הסר", "remove_assets_album_confirmation": "האם באמת ברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהאלבום?", "remove_assets_shared_link_confirmation": "האם אתה בטוח שברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהקישור המשותף הזה?", @@ -1543,19 +1585,25 @@ "reset_password": "איפוס סיסמה", "reset_people_visibility": "אפס את נראות האנשים", "reset_pin_code": "אפס קוד PIN", + "reset_sqlite": "אפס את מסד הנתונים SQLite", + "reset_sqlite_confirmation": "האם אתה בטוח שברצונך לאפס את מסד הנתונים SQLite? יהיה עליך להתנתק ולהתחבר מחדש כדי לסנכרן את הנתונים מחדש", + "reset_sqlite_success": "איפוס מסד הנתונים SQLite בוצע בהצלחה", "reset_to_default": "אפס לברירת מחדל", "resolve_duplicates": "פתור כפילויות", "resolved_all_duplicates": "כל הכפילויות נפתרו", "restore": "שחזר", "restore_all": "שחזר הכל", + "restore_trash_action_prompt": "{count} שוחזרו מהשאפה", "restore_user": "שחזר משתמש", "restored_asset": "התמונה שוחזרה", "resume": "המשך", "retry_upload": "נסה שוב להעלות", "review_duplicates": "בדוק כפילויות", + "review_large_files": "צפייה בקבצים גדולים", "role": "תפקיד", "role_editor": "עורך", "role_viewer": "צופה", + "running": "פועל", "save": "שמור", "save_to_gallery": "שמור לגלריה", "saved_api_key": "מפתח API שמור", @@ -1687,6 +1735,7 @@ "settings_saved": "ההגדרות נשמרו", "setup_pin_code": "הגדר קוד PIN", "share": "שתף", + "share_action_prompt": "שותפו {count} תמונות", "share_add_photos": "הוסף תמונות", "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", @@ -1708,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "הועתק ללוח", "shared_link_clipboard_text": "קישור: {password}\nסיסמה: {link}", "shared_link_create_error": "שגיאה ביצירת קישור משותף", + "shared_link_custom_url_description": "גש לקישור ששותף באמצעות כתובת URL מותאמת אישית", "shared_link_edit_description_hint": "הכנס את תיאור השיתוף", "shared_link_edit_expire_after_option_day": "1 יום", "shared_link_edit_expire_after_option_days": "{count} ימים", @@ -1733,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ניהול קישורים משותפים", "shared_link_options": "אפשרויות קישור משותף", + "shared_link_password_description": "דרוש סיסמה כדי לגשת לקישור המשותף הזה", "shared_links": "קישורים משותפים", "shared_links_description": "שתף תמונות וסרטונים עם קישור", "shared_photos_and_videos_count": "{assetCount, plural, other {# תמונות וסרטונים משותפים.}}", @@ -1788,6 +1839,7 @@ "sort_title": "כותרת", "source": "קוד מקור", "stack": "ערימה", + "stack_action_prompt": "{count} קובצו", "stack_duplicates": "צור ערימת כפילויות", "stack_select_one_photo": "בחר תמונה ראשית אחת עבור הערימה", "stack_selected_photos": "צור ערימת תמונות נבחרות", @@ -1807,6 +1859,7 @@ "storage_quota": "מכסת האחסון", "storage_usage": "{used} בשימוש מתוך {available}", "submit": "שלח", + "success": "בוצע בהצלחה", "suggestions": "הצעות", "sunrise_on_the_beach": "Sunrise on the beach (מומלץ לחפש באנגלית לתוצאות טובות יותר)", "support": "תמיכה", @@ -1816,6 +1869,8 @@ "sync": "סנכרן", "sync_albums": "סנכרן אלבומים", "sync_albums_manual_subtitle": "סנכרן את כל הסרטונים והתמונות שהועלו לאלבומי הגיבוי שנבחרו", + "sync_local": "סנכרן מקומי", + "sync_remote": "סנכרן מרוחק", "sync_upload_album_setting_subtitle": "צור והעלה תמונות וסרטונים שלך לאלבומים שנבחרו ביישום", "tag": "תג", "tag_assets": "תיוג תמונות", @@ -1826,6 +1881,7 @@ "tag_updated": "תג מעודכן: {tag}", "tagged_assets": "תויגו {count, plural, one {תמונה #} other {# תמונות}}", "tags": "תגים", + "tap_to_run_job": "לחץ על מנת להפעיל משימה", "template": "תבנית", "theme": "ערכת נושא", "theme_selection": "בחירת ערכת נושא", @@ -1898,16 +1954,20 @@ "unselect_all_duplicates": "בטל בחירת כל הכפילויות", "unselect_all_in": "בטל את הבחירה של הכל ב {group}", "unstack": "בטל ערימה", + "unstack_action_prompt": "{count} הופרדו", "unstacked_assets_count": "{count, plural, one {תמונה # הוסרה} other {# תמונות הוסרו}} מהערימה", "untagged": "לא מתיוגים", "up_next": "הבא בתור", "updated_at": "עודכן", "updated_password": "סיסמה עודכנה", "upload": "העלאה", + "upload_action_prompt": "{count} נוספו לתור להעלאה", "upload_concurrency": "בו-זמניות של העלאה", + "upload_details": "פרטי העלאה", "upload_dialog_info": "האם ברצונך לגבות את התמונות שנבחרו לשרת?", "upload_dialog_title": "העלאת תמונה", "upload_errors": "העלאה הושלמה עם {count, plural, one {שגיאה #} other {# שגיאות}}, רענן את הדף כדי לראות תמונות שהועלו.", + "upload_finished": "העלאה הסתיימה", "upload_progress": "נותרו {remaining, number} - טופלו {processed, number}/{total, number}", "upload_skipped_duplicates": "דילג על {count, plural, one {תמונה כפולה #} other {# תמונות כפולות}}", "upload_status_duplicates": "כפילויות", @@ -1916,6 +1976,7 @@ "upload_success": "ההעלאה בוצעה בהצלחה. רענן את הדף כדי לצפות בתמונות שהועלו.", "upload_to_immich": "העלה לשרת ({count})", "uploading": "מעלה", + "uploading_media": "מעלה מדיה", "url": "URL", "usage": "שימוש", "use_biometric": "השתמש באימות ביומטרי", @@ -1936,6 +1997,7 @@ "user_usage_stats_description": "הצג סטטיסטיקות שימוש בחשבון", "username": "שם משתמש", "users": "משתמשים", + "users_added_to_album_count": "נוספו {count, plural, one {משתמש #} other {# משתמשים}} לאלבום", "utilities": "כלים", "validate": "לאמת", "validate_endpoint_error": "נא להזין כתובת תקנית", @@ -1954,6 +2016,7 @@ "view_album": "הצג אלבום", "view_all": "הצג הכל", "view_all_users": "הצג את כל המשתמשים", + "view_details": "הצג פרטים", "view_in_timeline": "ראה בציר הזמן", "view_link": "הצג קישור", "view_links": "הצג קישורים", diff --git a/i18n/hi.json b/i18n/hi.json index 2cb29370b5..08c1933adc 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -45,7 +45,7 @@ "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "रखने के लिए पिछले डंप की मात्रा", "backup_settings": "डेटाबेस डंप सेटिंग्स", - "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें। ध्यान दें: इन कार्यों की निगरानी नहीं की जाती है और विफलता की स्थिति में आपको सूचित नहीं किया जाएगा।", + "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें।", "cleared_jobs": "{job}: के लिए कार्य साफ़ कर दिए गए", "config_set_by_file": "Config वर्तमान में एक config फ़ाइल द्वारा सेट किया गया है", "confirm_delete_library": "क्या आप वाकई {library} लाइब्रेरी को हटाना चाहते हैं?", @@ -166,12 +166,26 @@ "metadata_settings_description": "मेटाडेटा सेटिंग प्रबंधित करें", "migration_job": "प्रवास", "migration_job_description": "संपत्तियों और चेहरों के थंबनेल को नवीनतम फ़ोल्डर संरचना में माइग्रेट करें", + "nightly_tasks_cluster_faces_setting_description": "नए पहचाने गए चेहरों पर चेहरे की पहचान चलाएँ", + "nightly_tasks_cluster_new_faces_setting": "नए चेहरों को समूह में शामिल करें", + "nightly_tasks_database_cleanup_setting": "डेटाबेस क्लीनअप कार्य", + "nightly_tasks_database_cleanup_setting_description": "डेटाबेस से पुराना, समाप्त हो चुका डेटा साफ़ करें", + "nightly_tasks_generate_memories_setting": "यादें उत्पन्न करें", + "nightly_tasks_generate_memories_setting_description": "संपत्तियों से नई यादें बनाएँ", + "nightly_tasks_missing_thumbnails_setting": "गायब थंबनेल उत्पन्न करें", + "nightly_tasks_missing_thumbnails_setting_description": "थंबनेल निर्माण के लिए थंबनेल के बिना कतारबद्ध परिसंपत्तियाँ", + "nightly_tasks_settings": "रात्रिकालीन कार्य सेटिंग्स", + "nightly_tasks_settings_description": "रात्रिकालीन कार्यों का प्रबंधन करें", + "nightly_tasks_start_time_setting": "समय शुरू", + "nightly_tasks_start_time_setting_description": "वह समय जब सर्वर रात्रिकालीन कार्य चलाना शुरू करता है", + "nightly_tasks_sync_quota_usage_setting": "सिंक कोटा उपयोग", + "nightly_tasks_sync_quota_usage_setting_description": "वर्तमान उपयोग के आधार पर उपयोगकर्ता संग्रहण कोटा अपडेट करें", "no_paths_added": "कोई पथ नहीं डाला गया", "no_pattern_added": "कोई पैटर्न नहीं डाला गया", "note_apply_storage_label_previous_assets": "नोट: पहले अपलोड की गई संपत्तियों पर स्टोरेज लेबल लागू करने के लिए, चलाएँ", "note_cannot_be_changed_later": "नोट: इसे बाद में बदला नहीं जा सकता!", "notification_email_from_address": "इस पते से", - "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"", + "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"। यह सुनिश्चित करें कि आप उसी पते का उपयोग करें जिससे आपको ईमेल भेजने की अनुमति है।", "notification_email_host_description": "ईमेल सर्वर का होस्ट (उदा. smtp.immitch.app)", "notification_email_ignore_certificate_errors": "प्रमाणपत्र त्रुटियों पर ध्यान न दें", "notification_email_ignore_certificate_errors_description": "टीएलएस प्रमाणपत्र सत्यापन त्रुटियों पर ध्यान न दें (अनुशंसित नहीं)", @@ -195,7 +209,9 @@ "oauth_enable_description": "OAuth से लॉगिन करें", "oauth_mobile_redirect_uri": "मोबाइल रीडायरेक्ट यूआरआई", "oauth_mobile_redirect_uri_override": "मोबाइल रीडायरेक्ट यूआरआई ओवरराइड", - "oauth_mobile_redirect_uri_override_description": "सक्षम करें जब 'app.immitch:/' एक अमान्य रीडायरेक्ट यूआरआई हो।", + "oauth_mobile_redirect_uri_override_description": "जब OAuth प्रदाता किसी मोबाइल URI, जैसे ''{callback}'' की अनुमति नहीं देता, तब सक्षम करें", + "oauth_role_claim": "भूमिका का दावा", + "oauth_role_claim_description": "इस दावे की उपस्थिति के आधार पर स्वचालित रूप से व्यवस्थापक पहुँच प्रदान करें। दावे में 'उपयोगकर्ता' या 'व्यवस्थापक' हो सकता है।", "oauth_settings": "ओऑथ", "oauth_settings_description": "OAuth लॉगिन सेटिंग प्रबंधित करें", "oauth_settings_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें डॉक्स।", @@ -204,7 +220,7 @@ "oauth_storage_quota_claim": "भंडारण कोटा का दावा", "oauth_storage_quota_claim_description": "उपयोगकर्ता के संग्रहण कोटा को इस दावे के मूल्य पर स्वचालित रूप से सेट करें।", "oauth_storage_quota_default": "डिफ़ॉल्ट संग्रहण कोटा (GiB)", - "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो (असीमित कोटा के लिए 0 दर्ज करें)।", + "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो ।", "oauth_timeout": "ब्रेक का अनुरोध", "oauth_timeout_description": "अनुरोधों के लिए समय-सीमा मिलीसेकंड में", "password_enable_description": "ईमेल और पासवर्ड से लॉगिन करें", @@ -244,6 +260,7 @@ "storage_template_migration_info": "स्टोरेज टेम्प्लेट सभी एक्सटेंशन को लोअरकेस में बदल देगा। टेम्प्लेट में किए गए बदलाव सिर्फ़ नई संपत्तियों पर लागू होंगे। टेम्प्लेट को पहले अपलोड की गई संपत्तियों पर पूर्वव्यापी रूप से लागू करने के लिए, {job} चलाएँ।", "storage_template_migration_job": "संग्रहण टेम्पलेट माइग्रेशन कार्य", "storage_template_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें भंडारण टेम्पलेट और इसके आशय", + "storage_template_onboarding_description_v2": "सक्षम होने पर, यह सुविधा उपयोगकर्ता-निर्धारित टेम्पलेट के आधार पर फ़ाइलों को स्वतः व्यवस्थित करेगी। अधिक जानकारी के लिए, कृपया दस्तावेज़ीकरण देखें।", "storage_template_path_length": "अनुमानित पथ लंबाई सीमा: {length, number}/{limit, number}", "storage_template_settings": "भंडारण टेम्पलेट", "storage_template_settings_description": "अपलोड संपत्ति की फ़ोल्डर संरचना और फ़ाइल नाम प्रबंधित करें", @@ -356,10 +373,12 @@ "admin_password": "व्यवस्थापक पासवर्ड", "administration": "प्रशासन", "advanced": "विकसित", + "advanced_settings_beta_timeline_subtitle": "नए ऐप अनुभव को आज़माएँ", + "advanced_settings_beta_timeline_title": "बीटा टाइमलाइन", "advanced_settings_enable_alternate_media_filter_subtitle": "सिंक के दौरान वैकल्पिक मानदंडों के आधार पर मीडिया को फ़िल्टर करने के लिए इस विकल्प का उपयोग करें। इसे केवल तभी आज़माएँ जब आपको ऐप द्वारा सभी एल्बमों का पता लगाने में समस्या हो।", "advanced_settings_enable_alternate_media_filter_title": "[प्रयोगात्मक] वैकल्पिक डिवाइस एल्बम सिंक फ़िल्टर का उपयोग करें", "advanced_settings_log_level_title": "लॉग स्तर:{level}", - "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस पर मौजूद एसेट से थंबनेल लोड करने में काफ़ी समय लगता है। इसके बजाय रिमोट इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", + "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस स्थानीय एसेट से थंबनेल लोड करने में बहुत धीमे होते हैं। इसके बजाय, दूरस्थ इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", "advanced_settings_prefer_remote_title": "दूरस्थ छवियों को प्राथमिकता दें", "advanced_settings_proxy_headers_subtitle": "प्रत्येक नेटवर्क अनुरोध के साथ इम्मिच द्वारा भेजे जाने वाले प्रॉक्सी हेडर को परिभाषित करें", "advanced_settings_proxy_headers_title": "प्रॉक्सी हेडर", @@ -378,6 +397,7 @@ "album_cover_updated": "एल्बम कवर अपडेट किया गया", "album_delete_confirmation": "क्या आप वाकई एल्बम {album} हटाना चाहते हैं?", "album_delete_confirmation_description": "यदि यह एल्बम साझा किया गया है, तो अन्य उपयोगकर्ता इसे एक्सेस नहीं कर पाएंगे।", + "album_deleted": "एल्बम हटा दिया गया", "album_info_card_backup_album_excluded": "छोड़ा गया", "album_info_card_backup_album_included": "शामिल", "album_info_updated": "एल्बम की जानकारी अपडेट की गई", @@ -387,6 +407,7 @@ "album_options": "एल्बम विकल्प", "album_remove_user": "उपयोगकर्ता हटाएं?", "album_remove_user_confirmation": "क्या आप वाकई {user} को हटाना चाहते हैं?", + "album_search_not_found": "आपकी खोज से मेल खाता कोई एल्बम नहीं मिला", "album_share_no_users": "ऐसा लगता है कि आपने यह एल्बम सभी उपयोगकर्ताओं के साथ साझा कर दिया है या आपके पास साझा करने के लिए कोई उपयोगकर्ता नहीं है।", "album_updated": "एल्बम अपडेट किया गया", "album_updated_setting_description": "जब किसी साझा एल्बम में नई संपत्तियाँ हों तो एक ईमेल सूचना प्राप्त करें", @@ -402,7 +423,11 @@ "album_viewer_page_share_add_users": "उपयोगकर्ता जोड़ें", "album_with_link_access": "लिंक वाले किसी भी व्यक्ति को इस एल्बम में फ़ोटो और लोगों को देखने दें।", "albums": "एलबम", - "albums_count": "{गिनती, बहुवचन, एक {{count, number} एल्बम} अन्य {{count, number} एल्बम}}", + "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}", + "albums_default_sort_order": "डिफ़ॉल्ट एल्बम सॉर्ट क्रम", + "albums_default_sort_order_description": "नये एल्बम बनाते समय आरंभिक परिसंपत्ति सॉर्ट क्रम।", + "albums_feature_description": "परिसंपत्तियों का संग्रह जिसे अन्य उपयोगकर्ताओं के साथ साझा किया जा सकता है।", + "albums_on_device_count": "डिवाइस पर एल्बम ({count})", "all": "सभी", "all_albums": "सभी एलबम", "all_people": "सभी लोग", @@ -423,13 +448,14 @@ "app_settings": "एप्लिकेशन सेटिंग", "appears_in": "प्रकट होता है", "archive": "संग्रहालय", + "archive_action_prompt": "{count} को संग्रह में जोड़ा गया", "archive_or_unarchive_photo": "फ़ोटो को संग्रहीत या असंग्रहीत करें", "archive_page_no_archived_assets": "कोई संग्रहीत संपत्ति नहीं मिली", "archive_page_title": "पुरालेख ({count})", "archive_size": "पुरालेख आकार", "archive_size_description": "डाउनलोड के लिए संग्रह आकार कॉन्फ़िगर करें (GiB में)", "archived": "संग्रहित", - "archived_count": "{गणना, बहुवचन, अन्य {संग्रहीत #}}", + "archived_count": "{count, बहुवचन, अन्य {संग्रहीत #}}", "are_these_the_same_person": "क्या ये वही व्यक्ति हैं?", "are_you_sure_to_do_this": "क्या आप वास्तव में इसे करना चाहते हैं?", "asset_action_delete_err_read_only": "केवल पढ़ने योग्य परिसंपत्ति(ओं) को हटाया नहीं जा सकता, छोड़ा जा सकता है", @@ -442,50 +468,175 @@ "asset_hashing": "हैशिंग...।", "asset_list_group_by_sub_title": "द्वारा समूह बनाएं", "asset_list_layout_settings_dynamic_layout_title": "गतिशील लेआउट", + "asset_list_layout_settings_group_automatically": "स्वचालित", + "asset_list_layout_settings_group_by": "समूह परिसंपत्तियों द्वारा", + "asset_list_layout_settings_group_by_month_day": "महीना + दिन", + "asset_list_layout_sub_title": "लेआउट", + "asset_list_settings_subtitle": "फ़ोटो ग्रिड लेआउट सेटिंग्स", + "asset_list_settings_title": "चित्र की जाली", "asset_offline": "संपत्ति ऑफ़लाइन", "asset_offline_description": "यह संपत्ति ऑफ़लाइन है।", "asset_restored_successfully": "संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "asset_skipped": "छोड़ा गया", + "asset_skipped_in_trash": "कचरे में", "asset_uploaded": "अपलोड किए गए", - "asset_uploading": "अपलोड हो रहा है..।", + "asset_uploading": "अपलोड हो रहा है…", + "asset_viewer_settings_subtitle": "अपनी गैलरी व्यूअर सेटिंग प्रबंधित करें", + "asset_viewer_settings_title": "एसेट व्यूअर", "assets": "संपत्तियां", + "assets_added_count": "{count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_added_to_album_count": "एल्बम में {count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} को एल्बम में नहीं जोड़ा जा सकता", + "assets_count": "{count, बहुवचन, एक {# संपत्ति} अन्य {# संपत्ति}}", "assets_deleted_permanently": "{count} संपत्ति(याँ) स्थायी रूप से हटा दी गईं", "assets_deleted_permanently_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से स्थायी रूप से हटा दी गईं", + "assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}", + "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} अन्य {Downloaded # files successfully}}", + "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} को ट्रैश में ले जाया गया", + "assets_permanently_deleted_count": "स्थायी रूप से हटा दिया गया {count, plural, one {# asset} other {# assets}}", + "assets_removed_count": "{count, plural, one {# asset} other {# assets}} हटा दिया गया", "assets_removed_permanently_from_device": "{count} संपत्ति(याँ) आपके डिवाइस से स्थायी रूप से हटा दी गईं", - "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते।", + "assets_restored_count": "पुनर्स्थापित {count, plural, one {# asset} other {# assets}}", "assets_restored_successfully": "{count} संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "assets_trashed": "{count} संपत्ति(याँ) कचरे में डाली गईं", + "assets_trashed_count": "ट्रैश की गई {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से कचरे में डाली गईं", + "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}}एल्बम का पहले से ही हिस्सा थे", "authorized_devices": "अधिकृत उपकरण", + "automatic_endpoint_switching_subtitle": "उपलब्ध होने पर निर्दिष्ट वाई-फाई से स्थानीय रूप से कनेक्ट करें और अन्यत्र वैकल्पिक कनेक्शन का उपयोग करें", + "automatic_endpoint_switching_title": "स्वचालित URL स्विचिंग", + "autoplay_slideshow": "ऑटोप्ले स्लाइड शो", "back": "वापस", "back_close_deselect": "वापस जाएँ, बंद करें, या अचयनित करें", - "backup_controller_page_background_wifi": "Only on WiFi", + "background_location_permission": "पृष्ठभूमि स्थान अनुमति", + "background_location_permission_content": "पृष्ठभूमि में चलते समय नेटवर्क बदलने के लिए, Immich के पास *हमेशा* सटीक स्थान तक पहुंच होनी चाहिए ताकि ऐप वाई-फाई नेटवर्क का नाम पढ़ सके", + "backup": "बैकअप", + "backup_album_selection_page_albums_device": "डिवाइस पर एल्बम ({count})", + "backup_album_selection_page_albums_tap": "शामिल करने के लिए टैप करें, बाहर करने के लिए डबल टैप करें", + "backup_album_selection_page_assets_scatter": "एसेट कई एल्बमों में बिखरे हो सकते हैं। इसलिए, बैकअप प्रक्रिया के दौरान एल्बमों को शामिल या बाहर किया जा सकता है।", + "backup_album_selection_page_select_albums": "एल्बम चुनें", + "backup_album_selection_page_selection_info": "चयन जानकारी", + "backup_album_selection_page_total_assets": "कुल अद्वितीय संपत्तियाँ", + "backup_all": "सभी", + "backup_background_service_backup_failed_message": "संपत्तियों का बैकअप लेने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_connection_failed_message": "सर्वर से कनेक्ट करने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_current_upload_notification": "{filename} अपलोड हो रहा है", + "backup_background_service_default_notification": "नई परिसंपत्तियों की जांच की जा रही है…", + "backup_background_service_error_title": "बैकअप त्रुटि", + "backup_background_service_in_progress_notification": "अपनी परिसंपत्तियों का बैकअप लेना…", + "backup_background_service_upload_failure_notification": "{filename} अपलोड करने में विफल", + "backup_controller_page_albums": "बैकअप एल्बम", + "backup_controller_page_background_app_refresh_disabled_content": "बैकग्राउंड बैकअप का उपयोग करने के लिए सेटिंग्स > सामान्य > बैकग्राउंड ऐप रिफ्रेश में बैकग्राउंड ऐप रिफ्रेश सक्षम करें।", + "backup_controller_page_background_app_refresh_disabled_title": "पृष्ठभूमि ऐप रीफ़्रेश अक्षम", + "backup_controller_page_background_app_refresh_enable_button_text": "सेटिंग्स पर जाएँ", + "backup_controller_page_background_battery_info_link": "कैसे मुझे दिखाओ", + "backup_controller_page_background_battery_info_message": "सर्वोत्तम बैकग्राउंड बैकअप अनुभव के लिए, कृपया Immich के लिए बैकग्राउंड गतिविधि को प्रतिबंधित करने वाले किसी भी बैटरी ऑप्टिमाइज़ेशन को अक्षम करें।\n\nचूँकि यह डिवाइस-विशिष्ट है, इसलिए कृपया अपने डिवाइस निर्माता से आवश्यक जानकारी देखें।", + "backup_controller_page_background_battery_info_ok": "ठीक", + "backup_controller_page_background_battery_info_title": "बैटरी अनुकूलन", + "backup_controller_page_background_charging": "केवल चार्ज करते समय", + "backup_controller_page_background_configure_error": "पृष्ठभूमि सेवा कॉन्फ़िगर करने में विफल", + "backup_controller_page_background_delay": "नई संपत्ति का बैकअप विलंबित करें: {duration}", + "backup_controller_page_background_description": "ऐप खोले बिना किसी भी नई संपत्ति का स्वचालित रूप से बैकअप लेने के लिए पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_is_off": "स्वचालित पृष्ठभूमि बैकअप बंद है", + "backup_controller_page_background_is_on": "स्वचालित पृष्ठभूमि बैकअप चालू है", + "backup_controller_page_background_turn_off": "पृष्ठभूमि सेवा बंद करें", + "backup_controller_page_background_turn_on": "पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_wifi": "केवल वाई-फ़ाई पर", + "backup_controller_page_backup": "बैकअप", + "backup_controller_page_backup_selected": "चयनित: ", + "backup_controller_page_backup_sub": "बैकअप किए गए फ़ोटो और वीडियो", + "backup_controller_page_created": "निर्मित तिथि: {date}", + "backup_controller_page_desc_backup": "ऐप खोलते समय सर्वर पर नई संपत्तियों को स्वचालित रूप से अपलोड करने के लिए अग्रभूमि बैकअप चालू करें।", + "backup_controller_page_excluded": "छोड़ा गया: ", + "backup_controller_page_failed": "विफल ({count})", + "backup_controller_page_filename": "फ़ाइल का नाम: {{filename} [{size}]", + "backup_controller_page_id": "आईडी: {id}", + "backup_controller_page_info": "बैकअप जानकारी", + "backup_controller_page_none_selected": "कोई भी चयनित नहीं", + "backup_controller_page_remainder": "शेष", + "backup_controller_page_remainder_sub": "चयन से बैकअप लेने के लिए शेष फ़ोटो और वीडियो", + "backup_controller_page_server_storage": "सर्वर संग्रहण", + "backup_controller_page_start_backup": "बैकअप प्रारंभ करें", + "backup_controller_page_status_off": "स्वचालित अग्रभूमि बैकअप बंद है", + "backup_controller_page_status_on": "स्वचालित अग्रभूमि बैकअप चालू है", + "backup_controller_page_storage_format": "{कुल} में से {प्रयुक्त} प्रयुक्त", + "backup_controller_page_to_backup": "बैकअप किए जाने वाले एल्बम", + "backup_controller_page_total_sub": "चयनित एल्बमों से सभी अद्वितीय फ़ोटो और वीडियो", + "backup_controller_page_turn_off": "अग्रभूमि बैकअप बंद करें", + "backup_controller_page_turn_on": "अग्रभूमि बैकअप चालू करें", + "backup_controller_page_uploading_file_info": "फ़ाइल जानकारी अपलोड करना", + "backup_err_only_album": "एकमात्र एल्बम नहीं हटाया जा सकता", + "backup_info_card_assets": "संपत्ति", + "backup_manual_cancelled": "रद्द", + "backup_manual_in_progress": "अपलोड पहले से ही प्रगति पर है। कुछ देर बाद प्रयास करें", + "backup_manual_success": "सफलता", + "backup_manual_title": "अपलोड स्थिति", + "backup_options_page_title": "बैकअप विकल्प", + "backup_setting_subtitle": "पृष्ठभूमि और अग्रभूमि अपलोड सेटिंग प्रबंधित करें", "backward": "पिछला", + "beta_sync": "बीटा सिंक स्थिति", + "beta_sync_subtitle": "नए सिंक सिस्टम का प्रबंधन करें", + "biometric_auth_enabled": "बायोमेट्रिक प्रमाणीकरण सक्षम", + "biometric_locked_out": "आप बायोमेट्रिक प्रमाणीकरण से बाहर हैं", + "biometric_no_options": "कोई बायोमेट्रिक विकल्प उपलब्ध नहीं है", + "biometric_not_available": "इस डिवाइस पर बायोमेट्रिक प्रमाणीकरण उपलब्ध नहीं है", "birthdate_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "birthdate_set_description": "जन्मतिथि का उपयोग फोटो के समय इस व्यक्ति की आयु की गणना करने के लिए किया जाता है।", "blurred_background": "धुंधली पृष्ठभूमि", + "bugs_and_feature_requests": "बग और सुविधा अनुरोध", "build": "निर्माण", "build_image": "छवि बनाएँ", + "bulk_delete_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क में हटाना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी संपत्ति बनी रहेगी और बाकी सभी डुप्लिकेट हमेशा के लिए हट जाएँगे। आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "bulk_keep_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} रखना चाहते हैं? इससे बिना कुछ हटाए सभी डुप्लिकेट ग्रुप हल हो जाएँगे।", + "bulk_trash_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क ट्रैश करना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी एसेट रहेगी और बाकी सभी डुप्लिकेट ट्रैश हो जाएँगे।", "buy": "इम्मीच खरीदो", + "cache_settings_clear_cache_button": "कैश को साफ़ करें", + "cache_settings_clear_cache_button_title": "ऐप का कैश साफ़ करता है। कैश के दोबारा बनने तक, यह ऐप के प्रदर्शन पर काफ़ी असर डालेगा।", + "cache_settings_duplicated_assets_clear_button": "स्पष्ट", + "cache_settings_duplicated_assets_subtitle": "ऐप द्वारा अनदेखा की गई तस्वीरें और वीडियो", + "cache_settings_duplicated_assets_title": "डुप्लिकेट संपत्तियां ({count})", + "cache_settings_statistics_album": "लाइब्रेरी थंबनेल", + "cache_settings_statistics_full": "पूर्ण चित्र", + "cache_settings_statistics_shared": "साझा किए गए एल्बम थंबनेल", + "cache_settings_statistics_thumbnail": "थंबनेल", + "cache_settings_statistics_title": "कैश उपयोग", + "cache_settings_subtitle": "Immich मोबाइल एप्लिकेशन के कैशिंग व्यवहार को नियंत्रित करें", "cache_settings_tile_subtitle": "स्थानीय संग्रहण के व्यवहार को नियंत्रित करें", "cache_settings_tile_title": "स्थानीय संग्रहण", + "cache_settings_title": "कैशिंग सेटिंग्स", "camera": "कैमरा", "camera_brand": "कैमरा ब्रांड", "camera_model": "कैमरा मॉडल", "cancel": "रद्द करना", "cancel_search": "खोज रद्द करें", + "canceled": "रद्द करना", + "canceling": "रद्द कर रहा है", "cannot_merge_people": "लोगों का विलय नहीं हो सकता", "cannot_undo_this_action": "आप इस क्रिया को पूर्ववत नहीं कर सकते!", "cannot_update_the_description": "विवरण अद्यतन नहीं किया जा सकता", + "cast": "ढालना", + "cast_description": "उपलब्ध कास्ट गंतव्यों को कॉन्फ़िगर करें", "change_date": "बदलाव दिनांक", + "change_description": "विवरण बदलें", + "change_display_order": "प्रदर्शन क्रम बदलें", "change_expiration_time": "समाप्ति समय बदलें", "change_location": "स्थान बदलें", "change_name": "नाम परिवर्तन करें", - "change_name_successfully": "नाम सफलतापूर्वक बदलें", + "change_name_successfully": "नाम सफलतापूर्वक बदला गया", "change_password": "पासवर्ड बदलें", "change_password_description": "यह या तो पहली बार है जब आप सिस्टम में साइन इन कर रहे हैं या आपका पासवर्ड बदलने का अनुरोध किया गया है।", + "change_password_form_confirm_password": "पासवर्ड की पुष्टि कीजिये", + "change_password_form_description": "नमस्ते {name},\n\nया तो आप पहली बार सिस्टम में साइन इन कर रहे हैं या फिर आपका पासवर्ड बदलने का अनुरोध किया गया है। कृपया नीचे नया पासवर्ड डालें।", + "change_password_form_new_password": "नया पासवर्ड", + "change_password_form_password_mismatch": "सांकेतिक शब्द मेल नहीं खाते", + "change_password_form_reenter_new_password": "नया पासवर्ड पुनः दर्ज करें", + "change_pin_code": "पिन कोड बदलें", "change_your_password": "अपना पासवर्ड बदलें", "changed_visibility_successfully": "दृश्यता सफलतापूर्वक परिवर्तित", + "check_corrupt_asset_backup": "दूषित परिसंपत्ति बैकअप की जाँच करें", + "check_corrupt_asset_backup_button": "जाँच करें", + "check_corrupt_asset_backup_description": "यह जाँच केवल वाई-फ़ाई पर ही करें और सभी संपत्तियों का बैकअप लेने के बाद ही करें। इस प्रक्रिया में कुछ मिनट लग सकते हैं।", "check_logs": "लॉग जांचें", "choose_matching_people_to_merge": "मर्ज करने के लिए मिलते-जुलते लोगों को चुनें", "city": "शहर", @@ -494,21 +645,49 @@ "clear_all_recent_searches": "सभी हालिया खोजें साफ़ करें", "clear_message": "स्पष्ट संदेश", "clear_value": "स्पष्ट मूल्य", + "client_cert_dialog_msg_confirm": "ठीक", + "client_cert_enter_password": "पास वर्ड दर्ज करें", + "client_cert_import": "आयात", + "client_cert_import_success_msg": "क्लाइंट प्रमाणपत्र आयात किया गया है", + "client_cert_invalid_msg": "अमान्य प्रमाणपत्र फ़ाइल या गलत पासवर्ड", + "client_cert_remove_msg": "क्लाइंट प्रमाणपत्र हटा दिया गया है", + "client_cert_subtitle": "केवल PKCS12 (.p12, .pfx) फ़ॉर्मैट का समर्थन करता है। प्रमाणपत्र आयात/हटाएँ केवल लॉगिन से पहले उपलब्ध हैं", + "client_cert_title": "SSL क्लाइंट प्रमाणपत्र", + "clockwise": "दक्षिणावर्त", "close": "बंद", "collapse": "गिर जाना", "collapse_all": "सभी को संकुचित करें", + "color": "रंग", "color_theme": "रंग थीम", "comment_deleted": "टिप्पणी हटा दी गई", "comment_options": "टिप्पणी विकल्प", "comments_and_likes": "टिप्पणियाँ और पसंद", "comments_are_disabled": "टिप्पणियाँ अक्षम हैं", + "common_create_new_album": "नया एल्बम बनाएँ", + "common_server_error": "कृपया अपने नेटवर्क कनेक्शन की जांच करें, सुनिश्चित करें कि सर्वर पहुंच योग्य है और ऐप/सर्वर संस्करण संगत हैं।", + "completed": "पुरा होना", "confirm": "पुष्टि", "confirm_admin_password": "एडमिन पासवर्ड की पुष्टि करें", + "confirm_delete_face": "क्या आप वाकई एसेट से {name} चेहरा हटाना चाहते हैं?", "confirm_delete_shared_link": "क्या आप वाकई इस साझा लिंक को हटाना चाहते हैं?", + "confirm_keep_this_delete_others": "इस एसेट को छोड़कर, स्टैक की सभी अन्य एसेट हटा दी जाएँगी। क्या आप वाकई जारी रखना चाहते हैं?", + "confirm_new_pin_code": "नए पिन कोड की पुष्टि करें", "confirm_password": "पासवर्ड की पुष्टि कीजिये", + "confirm_tag_face": "क्या आप इस चेहरे को {name} के रूप में टैग करना चाहते हैं?", + "confirm_tag_face_unnamed": "क्या आप इस चेहरे को टैग करना चाहते हैं?", + "connected_device": "कनेक्टेड डिवाइस", + "connected_to": "से जुड़ा", "contain": "समाहित", "context": "संदर्भ", "continue": "जारी", + "control_bottom_app_bar_create_new_album": "नया एल्बम बनाएँ", + "control_bottom_app_bar_delete_from_immich": "Immich से हटाएं", + "control_bottom_app_bar_delete_from_local": "डिवाइस से हटाएं", + "control_bottom_app_bar_edit_location": "स्थान संपादित करें", + "control_bottom_app_bar_edit_time": "तारीख और समय संपादित करें", + "control_bottom_app_bar_share_link": "लिंक शेयर करें", + "control_bottom_app_bar_share_to": "साझा करें", + "control_bottom_app_bar_trash_from_immich": "ट्रैश में ले जाएं", "copied_image_to_clipboard": "छवि को क्लिपबोर्ड पर कॉपी किया गया।", "copied_to_clipboard": "क्लिपबोर्ड पर नकल!", "copy_error": "प्रतिलिपि त्रुटि", @@ -523,6 +702,7 @@ "covers": "आवरण", "create": "तैयार करें", "create_album": "एल्बम बनाओ", + "create_album_page_untitled": "शीर्षकहीन", "create_library": "लाइब्रेरी बनाएं", "create_link": "लिंक बनाएं", "create_link_to_share": "शेयर करने के लिए लिंक बनाएं", @@ -531,39 +711,75 @@ "create_new_person": "नया व्यक्ति बनाएं", "create_new_person_hint": "चयनित संपत्तियों को एक नए व्यक्ति को सौंपें", "create_new_user": "नया उपयोगकर्ता बनाएं", + "create_shared_album_page_share_add_assets": "संपत्ति जोड़ें", + "create_shared_album_page_share_select_photos": "फ़ोटो चुनें", + "create_tag": "टैग बनाएँ", + "create_tag_description": "एक नया टैग बनाएँ। नेस्टेड टैग के लिए, कृपया फ़ॉरवर्ड स्लैश सहित टैग का पूरा पथ दर्ज करें।", "create_user": "उपयोगकर्ता बनाइये", "created": "बनाया", + "created_at": "बनाया था", "crop": "छाँटें", + "curated_object_page_title": "चीज़ें", "current_device": "वर्तमान उपकरण", + "current_pin_code": "वर्तमान पिन कोड", + "current_server_address": "वर्तमान सर्वर पता", "custom_locale": "कस्टम लोकेल", "custom_locale_description": "भाषा और क्षेत्र के आधार पर दिनांक और संख्याएँ प्रारूपित करें", + "daily_title_text_date": "ई, एमएमएम डीडी", + "daily_title_text_date_year": "ई, एमएमएम दिन, वर्ष", "dark": "डार्क", + "dark_theme": "डार्क थीम टॉगल करें", "date_after": "इसके बाद की तारीख", "date_and_time": "तिथि और समय", "date_before": "पहले की तारीख", + "date_format": "ई, एलएलएल डी, वाई • एच:एमएम ए", "date_of_birth_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "date_range": "तिथि सीमा", "day": "दिन", "deduplicate_all": "सभी को डुप्लिकेट करें", + "deduplication_criteria_1": "छवि का आकार बाइट्स में", + "deduplication_criteria_2": "EXIF डेटा की संख्या", + "deduplication_info": "डुप्लीकेशन हटाने की जानकारी", + "deduplication_info_description": "परिसंपत्तियों का स्वचालित रूप से पूर्व-चयन करने और डुप्लिकेट को थोक में हटाने के लिए, हम निम्न पर ध्यान देते हैं:", "default_locale": "डिफ़ॉल्ट स्थान", "default_locale_description": "अपने ब्राउज़र स्थान के आधार पर दिनांक और संख्याएँ प्रारूपित करें", "delete": "हटाएँ", + "delete_action_prompt": "{count} स्थायी रूप से हटा दिया गया", "delete_album": "एल्बम हटाएँ", "delete_api_key_prompt": "क्या आप वाकई इस एपीआई कुंजी को हटाना चाहते हैं?", + "delete_dialog_alert": "ये आइटम Immich और आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_alert_local": "ये आइटम आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे, लेकिन फिर भी Immich सर्वर पर उपलब्ध रहेंगे", + "delete_dialog_alert_local_non_backed_up": "कुछ आइटम का Immich में बैकअप नहीं लिया गया है और उन्हें आपके डिवाइस से स्थायी रूप से हटा दिया जाएगा", + "delete_dialog_alert_remote": "ये आइटम Immich सर्वर से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_ok_force": "फिर भी हटाएं", + "delete_dialog_title": "स्थायी रूप से हटाएँ", "delete_duplicates_confirmation": "क्या आप वाकई इन डुप्लिकेट को स्थायी रूप से हटाना चाहते हैं?", + "delete_face": "चेहरा हटाएं", "delete_key": "कुंजी हटाएँ", "delete_library": "लाइब्रेरी हटाएँ", "delete_link": "लिंक हटाएँ", + "delete_local_action_prompt": "{count} स्थानीय रूप से हटा दिया गया", + "delete_local_dialog_ok_backed_up_only": "केवल बैकअप हटाएं", + "delete_local_dialog_ok_force": "फिर भी हटाएं", + "delete_others": "अन्य को हटाएँ", "delete_shared_link": "साझा किए गए लिंक को हटाएं", "delete_shared_link_dialog_title": "साझा किए गए लिंक को हटाएं", + "delete_tag": "टैग हटाएं", + "delete_tag_confirmation_prompt": "क्या आप वाकई {tagName} टैग हटाना चाहते हैं?", "delete_user": "उपभोक्ता मिटायें", "deleted_shared_link": "साझा किया गया लिंक हटा दिया गया", + "deletes_missing_assets": "डिस्क से गायब संपत्तियों को हटाता है", "description": "वर्णन", + "description_input_hint_text": "विवरण जोड़ें..।", + "description_input_submit_error": "विवरण अपडेट करते समय त्रुटि हुई, अधिक जानकारी के लिए लॉग देखें", + "deselect_all": "सबको अचयनित करो", "details": "विवरण", "direction": "दिशा", "disabled": "अक्षम", "disallow_edits": "संपादनों की अनुमति न दें", + "discord": "डिसकॉर्ड", "discover": "खोजें", + "discovered_devices": "खोजे गए उपकरण", "dismiss_all_errors": "सभी त्रुटियाँ ख़ारिज करें", "dismiss_error": "त्रुटि ख़ारिज करें", "display_options": "प्रदर्शन चुनाव", @@ -571,14 +787,18 @@ "display_original_photos": "मूल फ़ोटो प्रदर्शित करें", "display_original_photos_setting_description": "किसी संपत्ति को देखते समय थंबनेल के बजाय मूल तस्वीर प्रदर्शित करना पसंद करें जब मूल संपत्ति वेब-संगत हो।", "do_not_show_again": "इस संदेश को दुबारा मत दिखाना", + "documentation": "प्रलेखन", "done": "ठीक है", "download": "डाउनलोड करें", + "download_action_prompt": "{count} संपत्तियां डाउनलोड हो रही हैं", "download_canceled": "डाउनलोड रद्द कर दिया गया", "download_complete": "डाउनलोड पूरा", "download_enqueue": "डाउनलोड कतार में है", "download_error": "डाउनलोड त्रुटि", "download_failed": "डाउनलोड विफल", "download_finished": "डाउनलोड समाप्त", + "download_include_embedded_motion_videos": "एम्बेडेड वीडियो", + "download_include_embedded_motion_videos_description": "मोशन फ़ोटो में एम्बेड किए गए वीडियो को एक अलग फ़ाइल के रूप में शामिल करें", "download_notfound": "डाउनलोड नहीं मिला", "download_paused": "डाउनलोड स्थगित", "download_settings": "डाउनलोड करना", @@ -588,6 +808,7 @@ "download_sucess_android": "मीडिया DCIM/Immich में डाउनलोड हो गया है", "download_waiting_to_retry": "पुनः प्रयास करने का इंतजार कर रहा है", "downloading": "डाउनलोड", + "downloading_asset_filename": "संपत्ति {filename} डाउनलोड हो रही है", "downloading_media": "मीडिया डाउनलोड हो रहा है", "drop_files_to_upload": "अपलोड करने के लिए फ़ाइलें कहीं भी छोड़ें", "duplicates": "डुप्लिकेट", @@ -598,6 +819,7 @@ "edit_avatar": "अवतार को एडिट करें", "edit_date": "संपादन की तारीख", "edit_date_and_time": "दिनांक और समय संपादित करें", + "edit_description": "संपादित करें वर्णन", "edit_exclusion_pattern": "बहिष्करण पैटर्न संपादित करें", "edit_faces": "चेहरे संपादित करें", "edit_import_path": "आयात पथ संपादित करें", @@ -816,7 +1038,6 @@ "library_options": "पुस्तकालय विकल्प", "light": "रोशनी", "like_deleted": "जैसे हटा दिया गया", - "link_options": "लिंक विकल्प", "link_to_oauth": "OAuth से लिंक करें", "linked_oauth_account": "लिंक किया गया OAuth खाता", "list": "सूची", diff --git a/i18n/hr.json b/i18n/hr.json index 35e7aba7e0..fd17e12dc2 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podržano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Da biste zanemarili sve datoteke koje završavaju na \".tif\", koristite \"**/*.tif\". Da biste zanemarili apsolutni put, koristite \"/path/to/ignore/**\".", + "admin_user": "Administrator", "asset_offline_description": "Ovo sredstvo vanjske knjižnice više nije pronađeno na disku i premješteno je u smeće. Ako je datoteka premještena unutar biblioteke, provjerite svoju vremensku traku za novo odgovarajuće sredstvo. Da biste vratili ovo sredstvo, provjerite može li Immich pristupiti donjoj stazi datoteke i skenirajte biblioteku.", "authentication_settings": "Postavke autentikacije", "authentication_settings_description": "Uredi lozinku, OAuth, i druge postavke autentikacije", @@ -165,6 +166,20 @@ "metadata_settings_description": "Upravljanje postavkama metapodataka", "migration_job": "Migracija", "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", + "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novootkrivenim licima", + "nightly_tasks_cluster_new_faces_setting": "Grupiraj nova lica", + "nightly_tasks_database_cleanup_setting": "Zadaci čišćenja baze podataka", + "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", + "nightly_tasks_generate_memories_setting": "Generiraj uspomene", + "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz sadržaja", + "nightly_tasks_missing_thumbnails_setting": "Generiraj nedostajuće sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Stavi u red čekanja sadržaje bez sličica za generiranje sličica", + "nightly_tasks_settings": "Postavke noćnih zadataka", + "nightly_tasks_settings_description": "Upravljanje noćnim zadacima", + "nightly_tasks_start_time_setting": "Vrijeme početka", + "nightly_tasks_start_time_setting_description": "Vrijeme pokretanja noćnih zadataka na poslužitelju", + "nightly_tasks_sync_quota_usage_setting": "Sinkroniziraj iskorištenost kvote", + "nightly_tasks_sync_quota_usage_setting_description": "Ažuriraj korisničku kvotu za pohranu na temelju trenutne potrošnje", "no_paths_added": "Nema dodanih putanja", "no_pattern_added": "Nije dodan uzorak", "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", @@ -195,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "Nadjačavanje URI-preusmjeravanja za mobilne uređaje", "oauth_mobile_redirect_uri_override_description": "Omogući kada pružatelj OAuth ne dopušta mobilni URI, poput ''{callback}''", + "oauth_role_claim": "Dodjela uloge", + "oauth_role_claim_description": "Automatski dodijeli administratorski pristup na temelju prisutnosti ove tvrdnje. Tvrdnja može sadržavati ili 'user' ili 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje postavkama za prijavu kroz OAuth", "oauth_settings_more_details": "Za više pojedinosti o ovoj značajci pogledajte uputstva.", @@ -203,7 +220,7 @@ "oauth_storage_quota_claim": "Zahtjev za kvotom pohrane", "oauth_storage_quota_claim_description": "Automatski postavite korisničku kvotu pohrane na vrijednost ovog zahtjeva.", "oauth_storage_quota_default": "Zadana kvota pohrane (GiB)", - "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva (unesite 0 za neograničenu kvotu).", + "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva", "oauth_timeout": "Istek vremena zahtjeva", "oauth_timeout_description": "Istek vremena zahtjeva je u milisekundama", "password_enable_description": "Prijava s email adresom i zaporkom", @@ -243,6 +260,7 @@ "storage_template_migration_info": "Predložak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloška primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloška na prethodno prenesena sredstva, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije Predloška Pohrane", "storage_template_more_details": "Za više pojedinosti o ovoj značajci pogledajte Predložak pohrane i njegove implikacije", + "storage_template_onboarding_description_v2": "Kada je omogućena, ova će značajka automatski organizira datoteke prema predlošku koji je definirao korisnik. Za više informacija pogledajte dokumentaciju.", "storage_template_path_length": "Približno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "Predložak pohrane", "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", @@ -355,10 +373,12 @@ "admin_password": "Admin lozinka", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Isprobaj novo iskustvo aplikacije", + "advanced_settings_beta_timeline_title": "Beta vremenska crta", "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. Pokušajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", "advanced_settings_log_level_title": "Razina zapisivanja: {level}", - "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s resursa na uređaju. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih resursa. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mrežnim zahtjevom.", "advanced_settings_proxy_headers_title": "Proxy zaglavlja", @@ -377,6 +397,7 @@ "album_cover_updated": "Naslovnica albuma ažurirana", "album_delete_confirmation": "Jeste li sigurni da želite izbrisati album {album}?", "album_delete_confirmation_description": "Ako se ovaj album dijeli, drugi korisnici mu više neće moći pristupiti.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZUZETO", "album_info_card_backup_album_included": "UKLJUČENO", "album_info_updated": "Podaci o albumu ažurirani", @@ -386,6 +407,7 @@ "album_options": "Opcije albuma", "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da želite ukloniti {user}?", + "album_search_not_found": "Nema albuma koji odgovaraju vašem pretraživanju", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", "album_updated": "Album ažuriran", "album_updated_setting_description": "Primite obavijest e-poštom kada dijeljeni album ima nova sredstva", @@ -405,6 +427,7 @@ "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", + "albums_on_device_count": "Albumi na uređaju ({count})", "all": "Sve", "all_albums": "Svi albumi", "all_people": "Svi ljudi", @@ -425,6 +448,7 @@ "app_settings": "Postavke Aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", + "archive_action_prompt": "{count} dodano u arhivu", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", "archive_page_no_archived_assets": "Nema arhiviranih resursa", "archive_page_title": "Arhiviraj ({count})", @@ -462,10 +486,12 @@ "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Sadržaj se ne može dodati u album} other {{count} sadržaja se ne mogu dodati u album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich poslužitelja", + "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} other {Preuzeto je # datoteka – {error} datoteke nisu uspjele}}", + "assets_downloaded_successfully": "{count, plural, one {Uspješno preuzeta # datoteka} other {Uspješno preuzete # datoteke}}", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premješteno u smeće", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", @@ -480,10 +506,12 @@ "authorized_devices": "Ovlašteni Uređaji", "automatic_endpoint_switching_subtitle": "Povežite se lokalno preko naznačene Wi-Fi mreže kada je dostupna i koristite alternativne veze na drugim lokacijama", "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", + "autoplay_slideshow": "Automatsko prikazivanje slajdova", "back": "Nazad", "back_close_deselect": "Natrag, zatvorite ili poništite odabir", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Kako bi prebacivao mreže dok radi u pozadini, Immich mora *uvijek* imati pristup preciznoj lokaciji kako bi aplikacija mogla pročitati naziv Wi-Fi mreže", + "backup": "Sigurnosna kopija", "backup_album_selection_page_albums_device": "Albumi na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirnite za uključivanje, dvostruki dodir za isključivanje", "backup_album_selection_page_assets_scatter": "Resursi mogu biti raspoređeni u više albuma. Stoga, albumi mogu biti uključeni ili isključeni tijekom procesa sigurnosnog kopiranja.", @@ -547,6 +575,8 @@ "backup_options_page_title": "Opcije sigurnosnog kopiranja", "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", "backward": "Unazad", + "beta_sync": "Beta status sinkronizacije", + "beta_sync_subtitle": "Upravljaj novim sustavom sinkronizacije", "biometric_auth_enabled": "Biometrijska autentikacija omogućena", "biometric_locked_out": "Zaključani ste iz biometrijske autentikacije", "biometric_no_options": "Nema dostupnih biometrijskih opcija", @@ -564,7 +594,7 @@ "cache_settings_clear_cache_button": "Očisti predmemoriju", "cache_settings_clear_cache_button_title": "Briše predmemoriju aplikacije. Ovo će značajno utjecati na performanse aplikacije dok se predmemorija ponovno ne izgradi.", "cache_settings_duplicated_assets_clear_button": "OČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija ignorira", "cache_settings_duplicated_assets_title": "Duplicirani resursi ({count})", "cache_settings_statistics_album": "Sličice biblioteke", "cache_settings_statistics_full": "Pune slike", @@ -581,6 +611,7 @@ "cancel": "Otkaži", "cancel_search": "Otkaži pretragu", "canceled": "Otkazano", + "canceling": "Otkazivanje", "cannot_merge_people": "Nije moguće spojiti osobe", "cannot_undo_this_action": "Ne možete poništiti ovu radnju!", "cannot_update_the_description": "Nije moguće ažurirati opis", @@ -644,6 +675,7 @@ "confirm_password": "Potvrdite lozinku", "confirm_tag_face": "Želite li označiti ovo lice kao {name}?", "confirm_tag_face_unnamed": "Želite li označiti ovo lice?", + "connected_device": "Uređaj povezan", "connected_to": "Povezano s", "contain": "Sadrži", "context": "Kontekst", @@ -693,9 +725,11 @@ "current_server_address": "Trenutna adresa poslužitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", + "custom_url": "Prilagođena URL adresa", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Tamno", + "dark_theme": "Prebaci tamnu temu", "date_after": "Datum nakon", "date_and_time": "Datum i Vrijeme", "date_before": "Datum prije", @@ -711,6 +745,8 @@ "default_locale": "Zadana lokalizacija", "default_locale_description": "Oblikujte datume i brojeve na temelju jezika preglednika", "delete": "Izbriši", + "delete_action_confirmation_message": "Jeste li sigurni da želite izbrisati ovaj sadržaj? Ova radnja će premjestiti sadržaj u smeće na poslužitelju i upitat će vas želite li ga izbrisati i lokalno", + "delete_action_prompt": "{count} izbrisano", "delete_album": "Izbriši album", "delete_api_key_prompt": "Jeste li sigurni da želite izbrisati ovaj API ključ?", "delete_dialog_alert": "Ove stavke bit će trajno izbrisane iz Immicha i s vašeg uređaja", @@ -724,9 +760,12 @@ "delete_key": "Ključ za brisanje", "delete_library": "Izbriši knjižnicu", "delete_link": "Izbriši poveznicu", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo sigurnosno kopirane", "delete_local_dialog_ok_force": "Izbriši svejedno", "delete_others": "Izbriši druge", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši dijeljenu poveznicu", "delete_shared_link_dialog_title": "Izbriši dijeljenu poveznicu", "delete_tag": "Izbriši oznaku", @@ -737,12 +776,14 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Pogreška pri ažuriranju opisa, provjerite zapisnik za više detalja", + "deselect_all": "Poništi odabir svih", "details": "Detalji", "direction": "Smjer", "disabled": "Onemogućeno", "disallow_edits": "Zabrani izmjene", "discord": "Discord", "discover": "Otkrij", + "discovered_devices": "Otkriveni uređaji", "dismiss_all_errors": "Odbaci sve pogreške", "dismiss_error": "Odbaci pogrešku", "display_options": "Mogućnosti prikaza", @@ -753,6 +794,7 @@ "documentation": "Dokumentacija", "done": "Gotovo", "download": "Preuzmi", + "download_action_prompt": "Preuzimanje {count} sadržaja", "download_canceled": "Preuzimanje otkazano", "download_complete": "Preuzimanje završeno", "download_enqueue": "Preuzimanje dodano u red", @@ -790,6 +832,7 @@ "edit_key": "Ključ za uređivanje", "edit_link": "Uredi poveznicu", "edit_location": "Uredi lokaciju", + "edit_location_action_prompt": "{count} uređenih lokacija", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi ljude", @@ -808,6 +851,7 @@ "empty_trash": "Isprazni smeće", "empty_trash_confirmation": "Jeste li sigurni da želite isprazniti smeće? Time će se iz Immicha trajno ukloniti sva sredstva u otpadu.\nNe možete poništiti ovu radnju!", "enable": "Omogući", + "enable_backup": "Omogući sigurnosnu kopiju", "enable_biometric_auth_description": "Unesite svoj PIN kod za omogućavanje biometrijske autentikacije", "enabled": "Omogućeno", "end_date": "Datum završetka", @@ -964,6 +1008,8 @@ "explorer": "Pretraživač (Explorer)", "export": "Izvoz", "export_as_json": "Izvezi kao JSON", + "export_database": "Izvezi bazu podataka", + "export_database_description": "Izvezi SQLite bazu podataka", "extension": "Proširenje (Extension)", "external": "Vanjski", "external_libraries": "Vanjske Biblioteke", @@ -975,6 +1021,7 @@ "failed_to_load_assets": "Neuspjelo učitavanje stavki", "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", + "favorite_action_prompt": "{count} dodano u Omiljeno", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Omiljene", "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", @@ -1014,6 +1061,9 @@ "haptic_feedback_switch": "Omogući haptičku povratnu informaciju", "haptic_feedback_title": "Haptička povratna informacija", "has_quota": "Ima kvotu", + "hash_asset": "Hash sadržaja", + "hashed_assets": "Hashirani sadržaji", + "hashing": "Hashiranje", "header_settings_add_header_tip": "Dodaj zaglavlje", "header_settings_field_validator_msg": "Vrijednost ne može biti prazna", "header_settings_header_name_input": "Naziv zaglavlja", @@ -1046,6 +1096,7 @@ "host": "Domaćin", "hour": "Sat", "id": "ID", + "idle": "Neaktivan", "ignore_icloud_photos": "Ignoriraj iCloud fotografije", "ignore_icloud_photos_description": "Fotografije pohranjene na iCloudu neće biti učitane na Immich poslužitelj", "image": "Slika", @@ -1099,6 +1150,9 @@ "kept_this_deleted_others": "Zadržana je ova datoteka i izbrisano {count, plural, one {# datoteka} other {# datoteka}}", "keyboard_shortcuts": "Prečaci tipkovnice", "language": "Jezik", + "language_no_results_subtitle": "Pokušajte prilagoditi pojam za pretraživanje", + "language_no_results_title": "Nisu pronađeni jezici", + "language_search_hint": "Pretraži jezike...", "language_setting_description": "Odaberite željeni jezik", "last_seen": "Zadnji put viđen", "latest_version": "Najnovija verzija", @@ -1115,15 +1169,18 @@ "library_page_sort_created": "Datum kreiranja", "library_page_sort_last_modified": "Zadnja izmjena", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svjetlo", "like_deleted": "Like izbrisan", "link_motion_video": "Povežite videozapis pokreta", - "link_options": "Opcije veze", "link_to_oauth": "Veza na OAuth", "linked_oauth_account": "Povezani OAuth račun", "list": "Popis", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretraživanja nije uspjelo", + "local": "Lokalno", + "local_asset_cast_failed": "Nije moguće reproducirati sadržaj koji nije prenesen na poslužitelj", + "local_assets": "Lokalni sadržaji", "local_network": "Lokalna mreža", "local_network_sheet_info": "Aplikacija će se povezati s poslužiteljem putem ovog URL-a kada koristi određenu Wi-Fi mrežu", "location_permission": "Dozvola za lokaciju", @@ -1137,6 +1194,7 @@ "locked_folder": "Zaključana Mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", + "logged_in_as": "Prijavljeni kao {user}", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", @@ -1179,8 +1237,7 @@ "manage_your_devices": "Upravljajte uređajima na kojima ste prijavljeni", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Karta", - "map_assets_in_bound": "{count} fotografija", - "map_assets_in_bounds": "{count} fotografija", + "map_assets_in_bounds": "{count, plural, one {# fotografija} other {# fotografija}}", "map_cannot_get_user_location": "Nije moguće dohvatiti lokaciju korisnika", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Koristi ovu lokaciju", @@ -1232,6 +1289,7 @@ "more": "Više", "move": "Pomakni", "move_off_locked_folder": "Premjesti iz zaključane mape", + "move_to_lock_folder_action_prompt": "{count} dodano u zaključanu mapu", "move_to_locked_folder": "Premjesti u zaključanu mapu", "move_to_locked_folder_confirmation": "Ove fotografije i videozapis bit će uklonjeni iz svih albuma i bit će vidljivi samo iz zaključane mape", "moved_to_archive": "Premješteno {count, plural, one {# resurs} other {# resursa}} u arhivu", @@ -1264,6 +1322,7 @@ "no_archived_assets_message": "Arhivirajte fotografije i videozapise kako biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA PRENESETE SVOJU PRVU FOTOGRAFIJU", "no_assets_to_show": "Nema stavki za prikaz", + "no_cast_devices_found": "Nisu pronađeni uređaji za reprodukciju", "no_duplicates_found": "Nisu pronađeni duplikati.", "no_exif_info_available": "Nema dostupnih exif podataka", "no_explore_results_message": "Prenesite više fotografija da istražite svoju zbirku.", @@ -1277,6 +1336,7 @@ "no_results": "Nema rezultata", "no_results_description": "Pokušajte sa sinonimom ili općenitijom ključnom riječi", "no_shared_albums_message": "Stvorite album za dijeljenje fotografija i videozapisa s osobama u svojoj mreži", + "no_uploads_in_progress": "Nema aktivnih prijenosa", "not_in_any_album": "Ni u jednom albumu", "not_selected": "Nije odabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primijenili Oznaku za skladištenje na prethodno prenesena sredstva, pokrenite", @@ -1296,8 +1356,11 @@ "oldest_first": "Prvo najstarije", "on_this_device": "Na ovom uređaju", "onboarding": "Uključivanje (Onboarding)", - "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama administracije.", + "onboarding_locale_description": "Odaberite željeni jezik. Kasnije ga možete promijeniti u postavkama.", + "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama.", + "onboarding_server_welcome_description": "Postavimo vašu instancu s nekim uobičajenim postavkama.", "onboarding_theme_description": "Odaberite temu boja za svoj primjer. To možete kasnije promijeniti u postavkama.", + "onboarding_user_welcome_description": "Počnimo!", "onboarding_welcome_user": "Dobro došli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", @@ -1311,6 +1374,7 @@ "original": "originalno", "other": "Ostalo", "other_devices": "Ostali uređaji", + "other_entities": "Ostali entiteti", "other_variables": "Ostale varijable", "owned": "Vlasništvo", "owner": "Vlasnik", @@ -1442,6 +1506,7 @@ "purchase_server_description_2": "Status podupiratelja", "purchase_server_title": "Poslužitelj (Server)", "purchase_settings_server_activated": "Ključem proizvoda poslužitelja upravlja administrator", + "queue_status": "Stavljanje u red {count}/{total}", "rating": "Broj zvjezdica", "rating_clear": "Obriši ocjenu", "rating_count": "{count, plural, one {# zvijezda} other {# zvijezde}}", @@ -1470,6 +1535,8 @@ "refreshing_faces": "Osvježavanje lica", "refreshing_metadata": "Osvježavanje metapodataka", "regenerating_thumbnails": "Obnavljanje sličica", + "remote": "Udaljeno", + "remote_assets": "Udaljeni sadržaji", "remove": "Ukloni", "remove_assets_album_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz albuma?", "remove_assets_shared_link_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz ove dijeljene veze?", @@ -1477,12 +1544,15 @@ "remove_custom_date_range": "Ukloni prilagođeni datumski raspon", "remove_deleted_assets": "Ukloni izbrisana sredstva", "remove_from_album": "Ukloni iz albuma", + "remove_from_album_action_prompt": "{count} uklonjeno iz albuma", "remove_from_favorites": "Ukloni iz favorita", + "remove_from_lock_folder_action_prompt": "{count} uklonjeno iz zaključane mape", "remove_from_locked_folder": "Ukloni iz zaključane mape", "remove_from_locked_folder_confirmation": "Jeste li sigurni da želite premjestiti ove fotografije i videozapise iz zaključane mape? Bit će vidljivi u vašoj biblioteci.", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", "remove_memory": "Ukloni uspomenu", "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", + "remove_tag": "Ukloni oznaku", "remove_url": "Ukloni URL", "remove_user": "Ukloni korisnika", "removed_api_key": "Uklonjen API ključ: {name}", @@ -1504,11 +1574,15 @@ "reset_password": "Resetiraj lozinku", "reset_people_visibility": "Poništi vidljivost ljudi", "reset_pin_code": "Resetiraj PIN kod", + "reset_sqlite": "Resetiraj SQLite bazu podataka", + "reset_sqlite_confirmation": "Jeste li sigurni da želite resetirati SQLite bazu podataka? Morat ćete se odjaviti i ponovno prijaviti kako biste ponovno sinkronizirali podatke", + "reset_sqlite_success": "SQLite baza podataka je uspješno resetirana", "reset_to_default": "Vrati na zadano", "resolve_duplicates": "Riješite duplikate", "resolved_all_duplicates": "Razriješi sve duplikate", "restore": "Oporavi", "restore_all": "Oporavi sve", + "restore_trash_action_prompt": "{count} vraćeno iz smeća", "restore_user": "Vrati korisnika", "restored_asset": "Obnovljena datoteka", "resume": "Nastavi", @@ -1517,6 +1591,7 @@ "role": "Uloga", "role_editor": "Urednik", "role_viewer": "Gledatelj", + "running": "U tijeku", "save": "Spremi", "save_to_gallery": "Spremi u galeriju", "saved_api_key": "Spremljen API ključ", @@ -1589,6 +1664,7 @@ "select_album_cover": "Odaberite omot albuma", "select_all": "Odaberi sve", "select_all_duplicates": "Odaberi sve duplikate", + "select_all_in": "Odaberi sve u {group}", "select_avatar_color": "Odaberi boju avatara", "select_face": "Odaberi lice", "select_featured_photo": "Odaberi istaknutu fotografiju", @@ -1609,6 +1685,7 @@ "server_info_box_server_url": "URL poslužitelja", "server_offline": "Server izvan mreže", "server_online": "Server na mreži", + "server_privacy": "Privatnost poslužitelja", "server_stats": "Statistike servera", "server_version": "Verzija servera", "set": "Postavi", @@ -1618,6 +1695,7 @@ "set_date_of_birth": "Postavi datum rođenja", "set_profile_picture": "Postavi profilnu sliku", "set_slideshow_to_fullscreen": "Postavi prezentaciju na cijeli zaslon", + "set_stack_primary_asset": "Postavi kao glavni sadržaj", "setting_image_viewer_help": "Preglednik detalja prvo učitava malu sličicu, zatim učitava pregled srednje veličine (ako je omogućen), te na kraju učitava original (ako je omogućen).", "setting_image_viewer_original_subtitle": "Omogućite za učitavanje originalne slike pune rezolucije (velika!). Onemogućite za smanjenje potrošnje podataka (i mrežne i na predmemoriji uređaja).", "setting_image_viewer_original_title": "Učitaj originalnu sliku", @@ -1645,6 +1723,7 @@ "settings_saved": "Postavke su spremljene", "setup_pin_code": "Postavi PIN kod", "share": "Podijeli", + "share_action_prompt": "Podijeljeno {count} sadržaja", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} odabrano", "share_dialog_preparing": "Priprema...", @@ -1666,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik", "shared_link_clipboard_text": "Poveznica: {link}\nLozinka: {password}", "shared_link_create_error": "Pogreška pri kreiranju dijeljene poveznice", + "shared_link_custom_url_description": "Pristupite ovom dijeljenom linku pomoću prilagođene URL adrese", "shared_link_edit_description_hint": "Unesite opis dijeljenja", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dana", @@ -1691,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", "shared_link_options": "Opcije dijeljene poveznice", + "shared_link_password_description": "Zahtjevaj loziku za pristup ovom dijeljenom linku", "shared_links": "Dijeljene poveznice", "shared_links_description": "Podijelite fotografije i videozapise putem poveznice", "shared_photos_and_videos_count": "{assetCount, plural, =1 {# podijeljena fotografija ili videozapis.} few {# podijeljene fotografije i videozapisa.} other {# podijeljenih fotografija i videozapisa.}}", @@ -1746,6 +1827,7 @@ "sort_title": "Naslov", "source": "Izvor", "stack": "Složi", + "stack_action_prompt": "{count} složeno", "stack_duplicates": "Složi duplikate", "stack_select_one_photo": "Odaberi jednu glavnu fotografiju za slaganje", "stack_selected_photos": "Složi odabrane fotografije", @@ -1755,6 +1837,7 @@ "start_date": "Datum početka", "state": "Stanje", "status": "Status", + "stop_casting": "Zaustavi reprodukciju", "stop_motion_photo": "Zaustavi pokretnu fotografiju", "stop_photo_sharing": "Prestati dijeliti svoje fotografije?", "stop_photo_sharing_description": "{partner} više neće moći pristupiti vašim fotografijama.", @@ -1764,6 +1847,7 @@ "storage_quota": "Kvota Pohrane", "storage_usage": "{used} od {available} iskorišteno", "submit": "Pošalji", + "success": "Uspijeh", "suggestions": "Prijedlozi", "sunrise_on_the_beach": "Izlazak sunca na plaži", "support": "Podrška", @@ -1773,6 +1857,8 @@ "sync": "Sink.", "sync_albums": "Sinkroniziraj albume", "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", + "sync_local": "Sinkroniziraj lokalno", + "sync_remote": "Sinkroniziraj udaljeno", "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", "tag": "Oznaka", "tag_assets": "Označi stavke", @@ -1783,6 +1869,7 @@ "tag_updated": "Ažurirana oznaka: {tag}", "tagged_assets": "Označena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", "tags": "Oznake", + "tap_to_run_job": "Dodirnite za pokretanje zadatka", "template": "Predložak", "theme": "Tema", "theme_selection": "Izbor teme", @@ -1815,6 +1902,7 @@ "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Smeće", + "trash_action_prompt": "{count} premješteno u smeće", "trash_all": "Stavi sve u smeće", "trash_count": "Smeće {count, number}", "trash_delete_asset": "Premjesti u smeće / Izbriši stavku", @@ -1832,8 +1920,11 @@ "unable_to_change_pin_code": "Nije moguće promijeniti PIN kod", "unable_to_setup_pin_code": "Nije moguće postaviti PIN kod", "unarchive": "Poništi arhiviranje", + "unarchive_action_prompt": "{count} uklonjeno iz arhive", "unarchived_count": "{count, plural, =1 {Poništeno arhiviranje #} few {Poništeno arhiviranje #} other {Poništeno arhiviranje #}}", + "undo": "Poništi", "unfavorite": "Ukloni iz omiljenih", + "unfavorite_action_prompt": "{count} uklonjeno iz favorita", "unhide_person": "Prikaži osobu", "unknown": "Nepoznato", "unknown_country": "Nepoznata država", @@ -1849,16 +1940,22 @@ "unsaved_change": "Nespremljena promjena", "unselect_all": "Poništi odabir svih", "unselect_all_duplicates": "Poništi odabir svih duplikata", + "unselect_all_in": "Poništi odabir svih u {group}", "unstack": "Razdvoji", + "unstack_action_prompt": "{count} razloženo", "unstacked_assets_count": "Razdvojena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "untagged": "Bez oznaka", "up_next": "Sljedeće", "updated_at": "Ažurirano", "updated_password": "Lozinka ažurirana", "upload": "Prijenos", + "upload_action_prompt": "{count} u redu za prijenos", "upload_concurrency": "Istovremeni prijenosi", + "upload_details": "Detalji prijenosa", "upload_dialog_info": "Želite li sigurnosno kopirati odabranu stavku(e) na poslužitelj?", "upload_dialog_title": "Prenesi stavku", "upload_errors": "Prijenos završen s {count, plural, =1 {# greškom} few {# greške} other {# grešaka}}, osvježite stranicu da biste vidjeli nove prenesene stavke.", + "upload_finished": "Prijenos završen", "upload_progress": "Preostalo {remaining, number} - Obrađeno {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočena {count, plural, =1 {# duplicirana stavka} few {# duplicirane stavke} other {# dupliciranih stavki}}", "upload_status_duplicates": "Duplikati", @@ -1867,6 +1964,7 @@ "upload_success": "Prijenos uspješan, osvježite stranicu da biste vidjeli nove prenesene stavke.", "upload_to_immich": "Prenesi na Immich ({count})", "uploading": "Prijenos u tijeku", + "uploading_media": "Prijenos medija", "url": "URL", "usage": "Korištenje", "use_biometric": "Koristi biometriju", @@ -1878,6 +1976,7 @@ "user_liked": "{user} je označio/la sviđa mi se {type, select, photo {ovu fotografiju} video {ovaj videozapis} asset {ovu stavku} other {to}}", "user_pin_code_settings": "PIN kod", "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", + "user_privacy": "Privatnost korisnika", "user_purchase_settings": "Kupnja", "user_purchase_settings_description": "Upravljajte svojom kupnjom", "user_role_set": "Postavi {user} kao {role}", @@ -1886,6 +1985,7 @@ "user_usage_stats_description": "Pregledajte statistiku korištenja računa", "username": "Korisničko ime", "users": "Korisnici", + "users_added_to_album_count": "Dodan{o/a} {count, plural, one {# korisnik} few {# korisnika} other {# korisnika}} u album.", "utilities": "Alati", "validate": "Provjeri valjanost", "validate_endpoint_error": "Molimo unesite valjanu URL adresu", @@ -1904,6 +2004,7 @@ "view_album": "Prikaži album", "view_all": "Prikaži sve", "view_all_users": "Prikaži sve korisnike", + "view_details": "Prikaži pojedinosti", "view_in_timeline": "Prikaži na vremenskoj crti", "view_link": "Prikaži poveznicu", "view_links": "Prikaži poveznice", diff --git a/i18n/hu.json b/i18n/hu.json index df995c0d6c..71060b5330 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -167,11 +167,15 @@ "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_cluster_new_faces_setting": "Új arcok csoportosítása", "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", "nightly_tasks_generate_memories_setting": "Emlékek generálása", "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", + "nightly_tasks_settings": "Éjjeli Feladat Beállítások", + "nightly_tasks_settings_description": "Éjjeli feladatok kezelése", + "nightly_tasks_start_time_setting": "Kezdőidő", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", @@ -363,6 +367,7 @@ "admin_password": "Admin Jelszó", "administration": "Adminisztráció", "advanced": "Haladó", + "advanced_settings_beta_timeline_title": "Béta Idővonal", "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", @@ -385,6 +390,7 @@ "album_cover_updated": "Album borító frissítve", "album_delete_confirmation": "Biztos, hogy ki szeretnéd törölni a(z) {album} albumot?", "album_delete_confirmation_description": "Amennyiben ez egy megosztott album, a többi felhasználó sem fog tudni többé hozzáférni.", + "album_deleted": "Album törölve", "album_info_card_backup_album_excluded": "KIHAGYVA", "album_info_card_backup_album_included": "BELEÉRTVE", "album_info_updated": "Album infó frissítve", @@ -413,6 +419,7 @@ "albums_default_sort_order": "Alapértelmezett album rendezés", "albums_default_sort_order_description": "Alapértelmezett sorrendezés új albumok létrehozásánál.", "albums_feature_description": "Másokkal megosztható elemek gyűjteménye.", + "albums_on_device_count": "Albumok az eszközön ({count})", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden személy", @@ -474,6 +481,7 @@ "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem véglegesen törölve", "assets_deleted_permanently_from_server": "{count} elem véglegesen törölve az Immich szerverről", + "assets_downloaded_failed": "{count, plural, one {# fájl letöltve - {error} fájl sikertelen} other {# fájl letöltve - {error} fájl sikertelen}}", "assets_downloaded_successfully": "{count, plural, one {# fájl sikeresen letöltve} other {# fájl sikeresen letöltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} áthelyezve a lomtárba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} véglegesen törölve", @@ -493,6 +501,7 @@ "back_close_deselect": "Vissza, bezárás, vagy kijelölés törlése", "background_location_permission": "Háttérben történő helymeghatározási engedély", "background_location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "backup": "Mentés", "backup_album_selection_page_albums_device": "Ezen az eszközön lévő albumok ({count})", "backup_album_selection_page_albums_tap": "Koppints a hozzáadáshoz, duplán koppints az eltávolításhoz", "backup_album_selection_page_assets_scatter": "Egy elem több albumban is lehet. Ezért a mentéshez albumokat lehet hozzáadni vagy azokat a mentésből kihagyni.", @@ -556,6 +565,8 @@ "backup_options_page_title": "Biztonági mentés beállításai", "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", + "beta_sync": "Béta Szinkronizálás Állapota", + "beta_sync_subtitle": "Az új szinkronizálási rendszer kezelése", "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", @@ -572,7 +583,7 @@ "cache_settings_clear_cache_button": "Gyorsítótár kiürítése", "cache_settings_clear_cache_button_title": "Kiüríti az alkalmazás gyorsítótárát. Ez jelentősen kihat az alkalmazás teljesítményére, amíg a gyorsítótár újra nem épül.", "cache_settings_duplicated_assets_clear_button": "KIÜRÍT", - "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás fekete listára tett", + "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás figyelmen kívül hagyott", "cache_settings_duplicated_assets_title": "Duplikált Elemek ({count})", "cache_settings_statistics_album": "Képtár bélyegképei", "cache_settings_statistics_full": "Teljes méretű képek", @@ -703,6 +714,7 @@ "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "yyyy MMM dd (E)", "dark": "Sötét", + "dark_theme": "Sötét téma kapcsolása", "date_after": "Dátumtól", "date_and_time": "Dátum és Idő", "date_before": "Dátumig", @@ -718,6 +730,7 @@ "default_locale": "Alapértelmezett Területi Beállítás", "default_locale_description": "Dátumok és számok formázása a böngésződ területi beállítása alapján", "delete": "Törlés", + "delete_action_prompt": "{count} törölve", "delete_album": "Album törlése", "delete_api_key_prompt": "Biztosan törölni szeretnéd ezt az API kulcsot?", "delete_dialog_alert": "Ezek az elemek véglegesen törölve lesznek Immich-ről és az eszközödről is", @@ -731,9 +744,12 @@ "delete_key": "Kulcs törlése", "delete_library": "Képtár Törlése", "delete_link": "Link törlése", + "delete_local_action_prompt": "{count} törölve az eszközről", "delete_local_dialog_ok_backed_up_only": "Csak a Biztonsági Mentés Törlése", "delete_local_dialog_ok_force": "Törlés Mindenképp", "delete_others": "Többi törlése", + "delete_permanently": "Törlés véglegesen", + "delete_permanently_action_prompt": "{count} törölve véglegesen", "delete_shared_link": "Megosztott link törlése", "delete_shared_link_dialog_title": "Megosztott Link Törlése", "delete_tag": "Címke törlése", @@ -761,6 +777,7 @@ "documentation": "Dokumentáció", "done": "Kész", "download": "Letöltés", + "download_action_prompt": "{count} elem letöltése", "download_canceled": "Letöltés megszakítva", "download_complete": "Letöltés kész", "download_enqueue": "Letöltés sorba állítva", @@ -798,6 +815,7 @@ "edit_key": "Kulcs módosítása", "edit_link": "Link módosítása", "edit_location": "Hely módosítása", + "edit_location_action_prompt": "{count} hely változtatva", "edit_location_dialog_title": "Hely", "edit_name": "Név módosítása", "edit_people": "Személyek módosítása", @@ -816,6 +834,7 @@ "empty_trash": "Lomtár ürítése", "empty_trash_confirmation": "Biztosan kiüríted a lomtárat? Ez az Immich lomtárában lévő összes elemet véglegesen törli.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", + "enable_backup": "Biztonsági mentés bekapcsolása", "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítás engedélyezéséhez", "enabled": "Engedélyezve", "end_date": "Vég dátum", @@ -958,6 +977,7 @@ "exif_bottom_sheet_person_add_person": "Elnevez", "exif_bottom_sheet_person_age_months": "{months} hónap idős", "exif_bottom_sheet_person_age_year_months": "1 év, {months} hónap idős", + "exif_bottom_sheet_person_age_years": "Életkor: {years}", "exit_slideshow": "Kilépés a Diavetítésből", "expand_all": "Összes kinyitása", "experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt", @@ -971,6 +991,8 @@ "explorer": "Böngésző", "export": "Exportálás", "export_as_json": "Exportálás JSON formátumban", + "export_database": "Adatbázis Exportálása", + "export_database_description": "Az SQLite adatbázis exportálása", "extension": "Kiterjesztés", "external": "Külső Képtár", "external_libraries": "Külső Képtárak", @@ -1123,15 +1145,17 @@ "library_page_sort_created": "Létrehozás ideje", "library_page_sort_last_modified": "Utolsó módosítás ideje", "library_page_sort_title": "Album címe", + "licenses": "Licencek", "light": "Világos", "like_deleted": "Reakció törölve", "link_motion_video": "Motion videó hozzárendelése", - "link_options": "Link beállítások", "link_to_oauth": "Csatlakoztatás OAuth-hoz", "linked_oauth_account": "Csatlakoztatott OAuth felhasználó", "list": "Lista", "loading": "Betöltés", "loading_search_results_failed": "Keresési eredmények betöltése sikertelen", + "local": "Helyi", + "local_assets": "Helyi Elemek", "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", @@ -1188,8 +1212,7 @@ "manage_your_devices": "Bejelentkezett eszközök kezelése", "manage_your_oauth_connection": "OAuth kapcsolódás kezelése", "map": "Térkép", - "map_assets_in_bound": "{count} fotó", - "map_assets_in_bounds": "{count} fotó", + "map_assets_in_bounds": "{count, plural, one {# fotó} other {# fotó}}", "map_cannot_get_user_location": "A helymeghatározás nem sikerült", "map_location_dialog_yes": "Igen", "map_location_picker_page_use_location": "Kiválasztott hely használata", @@ -1478,6 +1501,8 @@ "refreshing_faces": "Arcok frissítése folyamatban", "refreshing_metadata": "Metaadatok frissítése folyamatban", "regenerating_thumbnails": "Bélyegképek újragenerálása folyamatban", + "remote": "Távoli", + "remote_assets": "Távoli Elemek", "remove": "Eltávolítás", "remove_assets_album_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} az albumból?", "remove_assets_shared_link_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", @@ -1513,6 +1538,7 @@ "reset_password": "Jelszó visszaállítása", "reset_people_visibility": "Személyek láthatóságának visszaállítása", "reset_pin_code": "PIN kód visszaállítása", + "reset_sqlite": "SQLite Adatbázis Visszaállítása", "reset_to_default": "Visszaállítás alapállapotba", "resolve_duplicates": "Duplikátumok feloldása", "resolved_all_duplicates": "Minden duplikátum feloldása", @@ -1583,7 +1609,7 @@ "search_places": "Helyek keresése", "search_rating": "Keresés értékelés szerint...", "search_result_page_new_search_hint": "Új Keresés", - "search_settings": "Keresési beállítások", + "search_settings": "Beállítások keresése", "search_state": "Megye/Állam keresése...", "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz ", "search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés", @@ -1774,6 +1800,7 @@ "storage_quota": "Tárhely kvóta", "storage_usage": "{used}/{available} használatban", "submit": "Beküldés", + "success": "Siker", "suggestions": "Javaslatok", "sunrise_on_the_beach": "Napkelte a tengerparton", "support": "Támogatás", @@ -1783,6 +1810,8 @@ "sync": "Szinkronizálás", "sync_albums": "Albumok szinkronizálása", "sync_albums_manual_subtitle": "Összes fotó és videó létrehozása és szinkronizálása a kiválasztott Immich albumokba", + "sync_local": "Helyi Szinkronizálása", + "sync_remote": "Távoli Szinkronizálása", "sync_upload_album_setting_subtitle": "Fotók és videók létrehozása és szinkronizálása a kiválasztott Immich albumba", "tag": "Címke", "tag_assets": "Elemek címkézése", diff --git a/i18n/hy.json b/i18n/hy.json index 86cbcd5554..3bb7e1f526 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -28,7 +28,6 @@ "exif_bottom_sheet_person_add_person": "Ավելացնել անուն", "exif_bottom_sheet_person_age_years": "Տարիք {years}", "hi_user": "Բարեւ {name} ({email})", - "map_assets_in_bound": "{count} նկար", "map_assets_in_bounds": "{count} նկարներ", "partner_list_user_photos": "{}-ին նկարները", "photos": "Նկարներ", diff --git a/i18n/id.json b/i18n/id.json index 92cb07ff30..7386887b5f 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -487,6 +487,7 @@ "authorized_devices": "Perangkat Terautentikasi", "back": "Kembali", "back_close_deselect": "Kembali, tutup, atau batalkan pemilihan", + "backup": "Cadangkan", "backup_album_selection_page_albums_device": "Album di perangkat ({count})", "backup_album_selection_page_albums_tap": "Sentuh untuk memilih, sentuh 2x untuk mengecualikan", "backup_album_selection_page_assets_scatter": "Aset dapat tersebar dalam banyak album. Sehingga album dapat dipilih atau dikecualikan saat proses pencadangan.", @@ -1069,7 +1070,6 @@ "light": "Terang", "like_deleted": "Suka dihapus", "link_motion_video": "Tautan video gerak", - "link_options": "Opsi tautan", "link_to_oauth": "Tautkan ke OAuth", "linked_oauth_account": "Akun OAuth tertaut", "list": "Daftar", @@ -1124,7 +1124,6 @@ "manage_your_devices": "Kelola perangkat Anda yang masuk", "manage_your_oauth_connection": "Kelola koneksi OAuth Anda", "map": "Peta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foto", "map_cannot_get_user_location": "Tidak dapat memeroleh lokasi pengguna", "map_location_dialog_yes": "Ya", diff --git a/i18n/it.json b/i18n/it.json index 0aec538a85..49ef0941e1 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -14,6 +14,7 @@ "add_a_location": "Aggiungi una posizione", "add_a_name": "Aggiungi un nome", "add_a_title": "Aggiungi un titolo", + "add_birthday": "Aggiungi un compleanno", "add_endpoint": "Aggiungi endpoint", "add_exclusion_pattern": "Aggiungi un pattern di esclusione", "add_import_path": "Aggiungi un percorso di importazione", @@ -44,6 +45,13 @@ "backup_database": "Crea Dump Database", "backup_database_enable_description": "Abilita i backup del database", "backup_keep_last_amount": "Numero di backup da mantenere", + "backup_onboarding_1_description": "copia offsite nel cloud o in un'altra sede fisica.", + "backup_onboarding_2_description": "copie locali su diversi dispositivi. Ciò include i file principali e un backup di tali file a livello locale.", + "backup_onboarding_3_description": "copie totali dei tuoi dati, compresi i file originali. Ciò include 1 copia offsite e 2 copie locali.", + "backup_onboarding_description": "Per proteggere i tuoi dati, è consigliato adottare una strategia di backup 3-2-1. Per una soluzione di backup completa, è consigliato conservare copie delle foto/video caricati e del database Immich.", + "backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consultare la documentazione.", + "backup_onboarding_parts_title": "Un backup 3-2-1 include:", + "backup_onboarding_title": "Backup", "backup_settings": "Impostazioni Dump database", "backup_settings_description": "Gestisci le impostazioni dei backup.", "cleared_jobs": "Cancellati i processi per: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", + "album_deleted": "Album eliminato", "album_info_card_backup_album_excluded": "ESCLUSI", "album_info_card_backup_album_included": "INCLUSI", "album_info_updated": "Informazioni dell'album aggiornate", @@ -510,6 +519,7 @@ "back_close_deselect": "Indietro, chiudi o deseleziona", "background_location_permission": "Permesso di localizzazione in background", "background_location_permission_content": "Per fare in modo che sia possibile cambiare rete quando è in esecuzione in background, Immich deve *sempre* avere accesso alla tua posizione precisa in modo da poter leggere il nome della rete Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Album sul dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere", "backup_album_selection_page_assets_scatter": "Visto che le risorse possono trovarsi in più album, questi possono essere inclusi o esclusi dal backup.", @@ -573,6 +583,8 @@ "backup_options_page_title": "Opzioni di Backup", "backup_setting_subtitle": "Gestisci le impostazioni di upload in primo piano e in background", "backward": "Indietro", + "beta_sync": "Status sincronizzazione beta", + "beta_sync_subtitle": "Gestisci il nuovo sistema di sincronizzazione", "biometric_auth_enabled": "Autenticazione biometrica attivata", "biometric_locked_out": "Sei stato bloccato dall'autenticazione biometrica", "biometric_no_options": "Nessuna opzione biometrica disponibile", @@ -721,6 +733,7 @@ "current_server_address": "Indirizzo del server in uso", "custom_locale": "Localizzazione personalizzata", "custom_locale_description": "Formatta data e numeri in base alla lingua e al paese", + "custom_url": "URL personalizzato", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", @@ -740,7 +753,8 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", - "delete_action_prompt": "{count} elementi eliminati definitivamente", + "delete_action_confirmation_message": "Vuoi davvero eliminare questo asset? Questa azione sposterà l'asset nel cestino del server e ti chiederà se desideri eliminarla localmente", + "delete_action_prompt": "{count} elementi eliminati", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -758,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", + "delete_permanently": "Elimina definitivamente", + "delete_permanently_action_prompt": "{count} eliminati definitivamente", "delete_shared_link": "Elimina link condiviso", "delete_shared_link_dialog_title": "Elimina link condiviso", "delete_tag": "Elimina tag", @@ -813,6 +829,7 @@ "edit": "Modifica", "edit_album": "Modifica album", "edit_avatar": "Modifica avatar", + "edit_birthday": "Modifica Compleanno", "edit_date": "Modifica data", "edit_date_and_time": "Modifica data e ora", "edit_description": "Modifica la descrizione", @@ -980,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Aggiungi una descrizione...", + "exif_bottom_sheet_description_error": "Errore durante l'aggiornamento della descrizione", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", @@ -1000,6 +1018,8 @@ "explorer": "Esplora", "export": "Esporta", "export_as_json": "Esporta come JSON", + "export_database": "Esporta database", + "export_database_description": "Esporta il database SQLite", "extension": "Estensione", "external": "Esterno", "external_libraries": "Librerie esterne", @@ -1051,6 +1071,9 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", + "hash_asset": "Risorsa hash", + "hashed_assets": "Risorse hash", + "hashing": "Hashing", "header_settings_add_header_tip": "Aggiungi Header", "header_settings_field_validator_msg": "Il valore non può essere vuoto", "header_settings_header_name_input": "Nome header", @@ -1083,6 +1106,7 @@ "host": "Host", "hour": "Ora", "id": "ID", + "idle": "Inattivo", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", "image": "Immagine", @@ -1140,6 +1164,7 @@ "language_no_results_title": "Linguaggi non trovati", "language_search_hint": "Cerca linguaggi...", "language_setting_description": "Seleziona la tua lingua predefinita", + "large_files": "File pesanti", "last_seen": "Ultimo accesso", "latest_version": "Ultima Versione", "latitude": "Latitudine", @@ -1159,13 +1184,14 @@ "light": "Chiaro", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", - "link_options": "Impostazioni Collegamento", "link_to_oauth": "Collegamento a OAuth", "linked_oauth_account": "Account OAuth collegato", "list": "Lista", "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", + "local": "Locale", "local_asset_cast_failed": "Impossibile trasmettere una risorsa che non è caricata sul server", + "local_assets": "Risorsa locale", "local_network": "Rete locale", "local_network_sheet_info": "L'app si collegherà al server tramite questo URL quando è in uso la rete Wi-Fi specificata", "location_permission": "Permesso di localizzazione", @@ -1222,8 +1248,7 @@ "manage_your_devices": "Gestisci i tuoi dispositivi collegati", "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto}}", "map_cannot_get_user_location": "Non è possibile ottenere la posizione dell'utente", "map_location_dialog_yes": "Si", "map_location_picker_page_use_location": "Usa questa posizione", @@ -1322,6 +1347,7 @@ "no_results": "Nessun risultato", "no_results_description": "Prova ad usare un sinonimo oppure una parola chiave più generica", "no_shared_albums_message": "Crea un album per condividere foto e video con le persone nella tua rete", + "no_uploads_in_progress": "Nessun upload in corso", "not_in_any_album": "In nessun album", "not_selected": "Non selezionato", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Per aggiungere l'etichetta dell'archiviazione agli asset caricati in precedenza, esegui", @@ -1359,6 +1385,7 @@ "original": "originale", "other": "Altro", "other_devices": "Altri dispositivi", + "other_entities": "Altre entità", "other_variables": "Altre variabili", "owned": "Posseduti", "owner": "Proprietario", @@ -1519,6 +1546,8 @@ "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", + "remote": "Remoto", + "remote_assets": "Risorse remote", "remove": "Rimuovi", "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} dall'album?", "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} da questo link condiviso?", @@ -1556,19 +1585,25 @@ "reset_password": "Ripristina password", "reset_people_visibility": "Ripristina visibilità persone", "reset_pin_code": "Resetta il codice PIN", + "reset_sqlite": "Resetta Database SQLite", + "reset_sqlite_confirmation": "Vuoi davvero reimpostare il database SQLite? Dovrai disconnetterti e riconnetterti per risincronizzare i dati", + "reset_sqlite_success": "Database SQLite reimpostato correttamente", "reset_to_default": "Ripristina i valori predefiniti", "resolve_duplicates": "Risolvi duplicati", "resolved_all_duplicates": "Tutti i duplicati sono stati risolti", "restore": "Ripristina", "restore_all": "Ripristina tutto", + "restore_trash_action_prompt": "{count} ripristinati dal cestino", "restore_user": "Ripristina utente", "restored_asset": "Asset ripristinato", "resume": "Riprendi", "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", + "review_large_files": "Revisiona file pesanti", "role": "Ruolo", "role_editor": "Editor", "role_viewer": "Visualizzatore", + "running": "In esecuzione", "save": "Salva", "save_to_gallery": "Salva in galleria", "saved_api_key": "Chiave API salvata", @@ -1722,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Copiato negli appunti", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Si è verificato un errore durante la creazione del link condiviso", + "shared_link_custom_url_description": "Accedi a questo link con un URL personalizzato", "shared_link_edit_description_hint": "Inserisci la descrizione della condivisione", "shared_link_edit_expire_after_option_day": "1 giorno", "shared_link_edit_expire_after_option_days": "{count} giorni", @@ -1747,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", + "shared_link_password_description": "Imposta una password per questo link", "shared_links": "Link condivisi", "shared_links_description": "Condividi foto e video con un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video condivisi.}}", @@ -1822,6 +1859,7 @@ "storage_quota": "Limite Archiviazione", "storage_usage": "{used} di {available} utilizzati", "submit": "Invia", + "success": "Successo", "suggestions": "Suggerimenti", "sunrise_on_the_beach": "Tramonto sulla spiaggia", "support": "Supporto", @@ -1831,6 +1869,8 @@ "sync": "Sincronizza", "sync_albums": "Sincronizza album", "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_local": "Sincronizza gli elementi locali", + "sync_remote": "Sincronizza gli elementi remoti", "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag": "Tag", "tag_assets": "Tagga risorse", @@ -1841,6 +1881,7 @@ "tag_updated": "Tag {tag} aggiornata", "tagged_assets": "{count, plural, one {# asset etichettato} other {# asset etichettati}}", "tags": "Tag", + "tap_to_run_job": "Tocca per eseguire l'attività", "template": "Modello", "theme": "Tema", "theme_selection": "Selezione tema", @@ -1895,6 +1936,7 @@ "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", + "unfavorite_action_prompt": "{count} rimossi dai Favoriti", "unhide_person": "Mostra persona", "unknown": "Sconosciuto", "unknown_country": "Paese sconosciuto", @@ -1912,15 +1954,20 @@ "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unselect_all_in": "Deseleziona tutto in {group}", "unstack": "Rimuovi dal gruppo", + "unstack_action_prompt": "{count} rimossi", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", + "untagged": "Senza tag", "up_next": "Prossimo", "updated_at": "Aggiornato il", "updated_password": "Password aggiornata", "upload": "Carica", + "upload_action_prompt": "{count} accodati per l'upload", "upload_concurrency": "Caricamenti contemporanei", + "upload_details": "Dettagli di caricamento", "upload_dialog_info": "Vuoi fare il backup sul server delle risorse selezionate?", "upload_dialog_title": "Carica file", "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli asset caricati.", + "upload_finished": "Upload terminato", "upload_progress": "Rimanenti {remaining, number} - Processati {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # asset duplicati}}", "upload_status_duplicates": "Duplicati", @@ -1929,6 +1976,7 @@ "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", "upload_to_immich": "Carica su Immich ({count})", "uploading": "Caricamento", + "uploading_media": "Caricando i media", "url": "URL", "usage": "Utilizzo", "use_biometric": "Usa biometrica", @@ -1949,6 +1997,7 @@ "user_usage_stats_description": "Consulta le statistiche d'uso dell'account", "username": "Nome utente", "users": "Utenti", + "users_added_to_album_count": "Aggiunti {count, plural, one {# utente} other {# utenti}} all'album", "utilities": "Utilità", "validate": "Validazione", "validate_endpoint_error": "Inserisci un URL valido", @@ -1967,6 +2016,7 @@ "view_album": "Visualizza Album", "view_all": "Vedi tutto", "view_all_users": "Visualizza tutti gli utenti", + "view_details": "Visualizza Dettagli", "view_in_timeline": "Visualizza in timeline", "view_link": "Visualizza link", "view_links": "Visualizza i link", diff --git a/i18n/ja.json b/i18n/ja.json index 69c0f368fa..fd4734febf 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -490,6 +490,7 @@ "back_close_deselect": "戻る、閉じる、選択解除", "background_location_permission": "バックグラウンド位置情報アクセス", "background_location_permission_content": "正常にWi-Fiの名前(SSID)を獲得するにはアプリが常に詳細な位置情報にアクセスできる必要があります", + "backup": "バックアップ", "backup_album_selection_page_albums_device": "デバイス上のアルバム({count})", "backup_album_selection_page_albums_tap": "タップで選択、ダブルタップで除外", "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ。 (同じ写真が複数のアルバムに登録されていることがあるため)", @@ -1133,7 +1134,6 @@ "light": "ライトモード", "like_deleted": "いいねが削除されました", "link_motion_video": "モーションビデオのリンク", - "link_options": "リンクのオプション", "link_to_oauth": "OAuthへリンクする", "linked_oauth_account": "リンクされたOAuthアカウント", "list": "リスト", @@ -1196,7 +1196,6 @@ "manage_your_devices": "ログインデバイスを管理します", "manage_your_oauth_connection": "OAuth接続を管理します", "map": "地図", - "map_assets_in_bound": "{count}枚", "map_assets_in_bounds": "{count}枚", "map_cannot_get_user_location": "位置情報がゲットできません", "map_location_dialog_yes": "はい", diff --git a/i18n/ko.json b/i18n/ko.json index 1890962e5a..ba6e3549fe 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -507,6 +507,7 @@ "back_close_deselect": "뒤로, 닫기, 선택 취소", "background_location_permission": "백그라운드 위치 권한", "background_location_permission_content": "백그라운드에서 네트워크를 전환하려면, Immich가 Wi-Fi 네트워크 이름을 확인할 수 있도록 '정확한 위치' 권한을 항상 허용해야 합니다.", + "backup": "백업", "backup_album_selection_page_albums_device": "기기의 앨범 ({count})", "backup_album_selection_page_albums_tap": "한 번 탭하면 포함되고, 두 번 탭하면 제외됩니다.", "backup_album_selection_page_assets_scatter": "각 항목은 여러 앨범에 포함될 수 있으며, 백업 진행 중에도 대상 앨범을 포함하거나 제외할 수 있습니다.", @@ -1130,7 +1131,6 @@ "light": "라이트", "like_deleted": "좋아요가 삭제되었습니다.", "link_motion_video": "모션 비디오 링크", - "link_options": "링크 옵션", "link_to_oauth": "OAuth에 연결", "linked_oauth_account": "OAuth 계정이 연결되었습니다.", "list": "목록", @@ -1191,7 +1191,6 @@ "manage_your_devices": "로그인된 기기 관리", "manage_your_oauth_connection": "OAuth 연결 관리", "map": "지도", - "map_assets_in_bound": "사진 {count}개", "map_assets_in_bounds": "사진 {count}개", "map_cannot_get_user_location": "사용자의 위치를 가져올 수 없습니다.", "map_location_dialog_yes": "예", diff --git a/i18n/lt.json b/i18n/lt.json index 14668921d2..5f58f3ffdc 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -95,6 +95,8 @@ "job_settings": "Užduočių nustatymai", "job_settings_description": "Keisti užduočių lygiagretumą", "job_status": "Užduočių būsenos", + "jobs_delayed": "{jobCount, plural, other {# delayed}}", + "jobs_failed": "{jobCount, plural, other {# failed}}", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka ištrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", @@ -167,13 +169,22 @@ "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpažinimą naujai aptiktiems veidams", "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", "nightly_tasks_database_cleanup_setting": "Duomenų bazės valymo darbai", + "nightly_tasks_database_cleanup_setting_description": "Išvalyti senus, nebgaliojančius duomenis iš duomenų bazės", + "nightly_tasks_generate_memories_setting": "Kurti prisiminimus", + "nightly_tasks_generate_memories_setting_description": "Iš duomenų kurti naujus prisiminimus", + "nightly_tasks_missing_thumbnails_setting": "Kurti trūkstamas miniatiūras", + "nightly_tasks_missing_thumbnails_setting_description": "Sudaryti įrašų be miniatiūrų eilę miniatiūrų generavimui", + "nightly_tasks_settings": "Naktinių užduočių nustatymai", + "nightly_tasks_settings_description": "Valdyti naktines užduotis", + "nightly_tasks_start_time_setting": "Pradžios laikas", + "nightly_tasks_start_time_setting_description": "Laikas kada serveris pradės vykdyti naktines užduotis", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos Žymą seniau įkeltiems ištekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "Iš adreso", - "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdžiui: \"Immich Photo Server \"", - "notification_email_host_description": "Elektroninio pašto serverio savininkas (pvz. smtp.immich.app)", + "notification_email_from_address_description": "Siuntėjo el. pašto adresas, pavyzdžiui: \"Immich Photo Server \". Būtinai naudokite adresą iš kurio jums galima siųsti laiškus.", + "notification_email_host_description": "Elektroninio pašto serverio adresas (pvz. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Nepaisyti sertifikatų klaidų", "notification_email_ignore_certificate_errors_description": "Nepaisyti TLS sertifikato patvirtinimo klaidų (nerekomenduojama)", "notification_email_password_description": "Slaptažodis, naudojant autentikacijai su elektroninio pašto serveriu", @@ -261,20 +272,32 @@ "template_settings": "Pranešimų šablonai", "template_settings_description": "Tvarkyti pasirinktinius pranešimų šablonus", "theme_custom_css_settings": "Individualizuotas CSS", + "theme_custom_css_settings_description": "CSS leidžiantis keisti Immich dizainą.", "theme_settings": "Temos nustatymai", + "theme_settings_description": "Valdyti Immich web sąsajos pritaikymus", "thumbnail_generation_job": "Generuoti Miniatiūras", "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", + "transcoding_acceleration_api_description": "API kurį naudos paspartintam perkodavimui. Tai veiks pagal \"geriausią bandymą\": nepavykus bus naudojamas programinis perkodavimas. VP9 gali veikti arba ne priklausomai nuo jūsų techninės įrangos.", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (reikalingas 7-os arba vėlesnės generacijos Intel procesorius)", + "transcoding_acceleration_rkmpp": "RKMPP (tik Rockchip SOCs)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "Priimtini garso kodekai", + "transcoding_accepted_audio_codecs_description": "Pasirinkti kurių garso kodekų nereikia perkoduoti. Naudojma tik kai kurioms perkodavimo taisyklėms.", "transcoding_accepted_containers": "Priimami konteineriai", + "transcoding_accepted_containers_description": "Pasirinkti kurių konteinerių formatų nereikia performuoti į MP4. Naudojama tik kai kurioms perkodavimo taisyklėms.", + "transcoding_accepted_video_codecs": "Priimami vaizdo kodekai", + "transcoding_accepted_video_codecs_description": "Pasirinkti vaizdo kodekus kurių nereikia perkoduoti. Naudojama tik kai kurioms perkodavimo taisyklėms.", "transcoding_advanced_options_description": "Parinktys, kurių daugelis naudotojų keisti neturėtų", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", + "transcoding_codecs_learn_more": "Sužinoti daugiau apie naudojamą terminologiją, naudokite FFmpeg dokumentaciją H.264 codec, HEVC codec and VP9 codec.", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", + "transcoding_constant_quality_mode_description": "ICQ yra geriau nei CPQ, tačiau ne visi įrenginiai palaiko šį spartinimo būdą. Šis pasirinkimas būtų naudojamas kai nustatytas Kodavimas Pagal Kokybę. NVENC nepalaiko šio pasirinkimo todėl bus ignoruojamas.", "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikšmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo mažesnis tuo kokybiškesnis tačiau didesni failai.", + "transcoding_disabled_description": "Nedaryti perkodavimo, įrašų peržiūra gali neveikti ant kai kūrių sąsajų", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", "transcoding_max_bitrate": "Maksimalus bitų srautas", @@ -646,7 +669,6 @@ "level": "Lygis", "library": "Biblioteka", "library_options": "Bibliotekos pasirinktys", - "link_options": "Nuorodų parinktys", "link_to_oauth": "Susieti su OAuth", "linked_oauth_account": "Susieta OAuth paskyra", "list": "Sąrašas", diff --git a/i18n/lv.json b/i18n/lv.json index 2e6b702e22..2c51cc3854 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -14,6 +14,7 @@ "add_a_location": "Pievienot atrašanās vietu", "add_a_name": "Pievienot vārdu", "add_a_title": "Pievienot virsrakstu", + "add_birthday": "Pievienot dzimšanas dienu", "add_endpoint": "Pievienot galapunktu", "add_exclusion_pattern": "Pievienot izslēgšanas šablonu", "add_import_path": "Pievienot importa ceļu", @@ -191,6 +192,7 @@ "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", "album_cover_updated": "Albuma attēls atjaunināts", + "album_deleted": "Albums dzēsts", "album_info_card_backup_album_excluded": "NEIEKĻAUTS", "album_info_card_backup_album_included": "IEKĻAUTS", "album_info_updated": "Albuma informācija atjaunināta", @@ -209,6 +211,10 @@ "album_viewer_appbar_share_to": "Kopīgot Uz", "album_viewer_page_share_add_users": "Pievienot lietotājus", "albums": "Albumi", + "albums_default_sort_order": "Albuma noklusējuma kārtošanas secība", + "albums_default_sort_order_description": "Sākotnējā failu kārtošanas secība, veidojot jaunus albumus.", + "albums_feature_description": "Failu kolekcijas, kuras var koplietot ar citiem lietotājiem.", + "albums_on_device_count": "Albumi ierīcē ({count})", "all": "Viss", "all_albums": "Visi albumi", "all_people": "Visi cilvēki", @@ -217,6 +223,7 @@ "allow_edits": "Atļaut labošanu", "allow_public_user_to_download": "Atļaut lejupielādēt publiskiem lietotājiem", "allow_public_user_to_upload": "Atļaut augšupielādēt publiskiem lietotājiem", + "alt_text_qr_code": "QR koda attēls", "anti_clockwise": "Pretēji pulksteņrādītāja virzienam", "api_key": "API atslēga", "api_key_description": "Šī vērtība tiks parādīta tikai vienu reizi. Nokopējiet to pirms loga aizvēršanas.", @@ -250,6 +257,7 @@ "authorized_devices": "Autorizētās ierīces", "automatic_endpoint_switching_title": "Automātiska URL pārslēgšana", "back": "Atpakaļ", + "backup": "Dublēšana", "backup_album_selection_page_albums_device": "Albumi ierīcē ({count})", "backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu", "backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.", @@ -335,6 +343,7 @@ "cache_settings_title": "Kešdarbes iestatījumi", "camera": "Fotokamera", "cancel": "Atcelt", + "canceling": "Atceļ", "cannot_merge_people": "Nevar apvienot cilvēkus", "change_date": "Mainīt datumu", "change_description": "Mainīt aprakstu", @@ -476,6 +485,7 @@ "empty_trash": "Iztukšot atkritni", "enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu", "end_date": "Beigu datums", + "enqueued": "Ierindots", "enter_wifi_name": "Ievadi Wi-Fi nosaukumu", "enter_your_pin_code": "Ievadi savu PIN kodu", "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekļūtu slēgtajai mapei", @@ -485,6 +495,8 @@ "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "failed_to_create_album": "Neizdevās izveidot albumu", + "import_path_already_exists": "Šis importa ceļš jau pastāv.", + "incorrect_email_or_password": "Nepareizs e-pasts vai parole", "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "unable_to_change_description": "Neizdevās nomainīt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", @@ -512,6 +524,8 @@ "expired": "Derīguma termiņš beidzās", "explore": "Izpētīt", "export": "Eksportēt", + "export_database": "Eksportēt datubāzi", + "export_database_description": "Eksportēt SQLite datubāzi", "extension": "Paplašinājums", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "failed_to_authenticate": "Neizdevās autentificēties", @@ -527,6 +541,10 @@ "folders": "Mapes", "gcast_enabled": "Google Cast", "get_help": "Saņemt palīdzību", + "getting_started": "Pirmie soļi", + "go_back": "Doties atpakaļ", + "go_to_folder": "Doties uz mapi", + "go_to_search": "Doties uz meklēšanu", "haptic_feedback_switch": "Iestatīt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", @@ -583,6 +601,7 @@ "language": "Valoda", "language_search_hint": "Meklēt valodas...", "language_setting_description": "Izvēlieties vēlamo valodu", + "large_files": "Lielie faili", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Ģeogrāfiskais platums", @@ -597,6 +616,7 @@ "library_page_sort_created": "Jaunākais izveidotais", "library_page_sort_last_modified": "Pēdējo reizi modificēts", "library_page_sort_title": "Albuma virsraksts", + "licenses": "Licences", "list": "Saraksts", "loading": "Ielādē", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", @@ -640,7 +660,6 @@ "manage_your_devices": "Pieslēgto ierīču pārvaldība", "manage_your_oauth_connection": "OAuth savienojumu pārvaldība", "map": "Karte", - "map_assets_in_bound": "{count} fotoattēls", "map_assets_in_bounds": "{count} fotoattēli", "map_cannot_get_user_location": "Nevar iegūt lietotāja atrašanās vietu", "map_location_dialog_yes": "Jā", @@ -834,6 +853,7 @@ "purchase_server_description_2": "Atbalstītāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", + "queue_status": "Ierindo {count}/{total}", "rating_clear": "Noņemt vērtējumu", "rating_description": "Rādīt EXIF vērtējumu informācijas panelī", "reaction_options": "Reakcijas iespējas", @@ -875,6 +895,7 @@ "resume": "Turpināt", "retry_upload": "Atkārtot augšupielādi", "review_duplicates": "Pārskatīt dublikātus", + "review_large_files": "Pārskatīt lielos failus", "role": "Loma", "role_editor": "Redaktors", "role_viewer": "Skatītājs", @@ -1090,12 +1111,15 @@ "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", "upload": "Augšupielādēt", + "upload_action_prompt": "{count} ierindoti augšupielādei", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktīva(-u) dublējumu uz servera?", "upload_dialog_title": "Augšupielādēt Aktīvu", + "upload_finished": "Augšupielāde pabeigta", "upload_status_duplicates": "Dublikāti", "upload_status_errors": "Kļūdas", "upload_status_uploaded": "Augšupielādēts", "upload_to_immich": "Augšupielādēt Immich ({count})", + "uploading_media": "Augšupielādē failus", "url": "URL", "usage": "Lietojums", "use_biometric": "Izmantot biometrisko autentifikāciju", diff --git a/i18n/mk.json b/i18n/mk.json index 31d223cbd6..938d2158da 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -199,7 +199,6 @@ "level": "Ниво", "library": "Библиотека", "light": "Светло", - "link_options": "Опции за линк", "list": "Листа", "loading": "Вчитување", "log_out": "Одјави се", diff --git a/i18n/ml.json b/i18n/ml.json index 8d25cb5604..8787367bb6 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -3,10 +3,71 @@ "account": "അക്കൗണ്ട്", "account_settings": "അക്കൗണ്ട് സെറ്റിംഗ്സ്", "acknowledge": "അംഗീകരിക്കുക", + "action": "ആക്ഷന്‍", + "action_common_update": "പുതുക്കുക", + "actions": "പ്രവർത്തികൾ", + "active": "സജീവമായവ", + "activity": "പ്രവർത്തനങ്ങൾ", "add": "ചേർക്കുക", "add_a_description": "ഒരു വിവരണം ചേർക്കുക", "add_a_location": "ഒരു സ്ഥലം ചേർക്കുക", "add_a_name": "ഒരു പേര് ചേർക്കുക", "add_a_title": "ഒരു ശീർഷകം ചേർക്കുക", - "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക" + "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക", + "add_exclusion_pattern": "ഒഴിവാക്കാനുള്ള മാതൃക ചേർക്കുക", + "add_import_path": "ഇറക്കുമതി ചെയ്യുക", + "add_location": "സ്ഥലനാമം ചേര്‍ക്കുക", + "add_more_users": "കൂടുതല്‍ ഉപയോക്താക്കളെ ചേര്‍ക്കുക", + "add_partner": "പങ്കാളിയെ ചേര്‍ക്കുക", + "add_path": "പാത ചേര്‍ക്കുക", + "add_photos": "ചിത്രങ്ങള്‍ ചേര്‍ക്കുക", + "add_tag": "ടാഗ് ചേര്‍ക്കുക", + "add_to": "ചേര്‍ക്കുക", + "add_to_album": "ആല്‍ബത്തിലേക്ക് ചേര്‍ക്കുക", + "add_to_album_bottom_sheet_added": "{album} - ലേക്ക് ചേര്‍ത്തു", + "add_to_album_bottom_sheet_already_exists": "{album} ആൽബത്തിൽ ഇപ്പോള്‍ തന്നെ ഉണ്ട്", + "add_to_shared_album": "പങ്കിട്ട ആൽബത്തിലേക്ക് ചേർക്കുക", + "add_url": "URL ചേര്‍ക്കുക", + "added_to_archive": "ചരിത്രരേഖയായി (ആര്‍ക്കൈവ്) ചേര്‍ത്തിരിക്കുന്നു", + "added_to_favorites": "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "added_to_favorites_count": "{count, number} ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "admin": { + "add_exclusion_pattern_description": "ഒഴിവാക്കൽ ചിഹ്നങ്ങള്‍ ചേർക്കുക. *, **, ? എന്നിവ ഉപയോഗിച്ചുള്ള ഗ്ലോബിംഗ് പിന്തുണയ്ക്കുന്നു. \"Raw\" എന്ന് പേരുള്ള ഏതെങ്കിലും ഡയറക്ടറിയിലെ എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/Raw/**\" ഉപയോഗിക്കുക. \".tif\" ൽ അവസാനിക്കുന്ന എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/*.tif\" ഉപയോഗിക്കുക. ഒരു പരിപൂർണ്ണമായ പാത അവഗണിക്കാൻ, \"/path/to/ignore/**\" ഉപയോഗിക്കുക.", + "admin_user": "ഭരണാധികാരി", + "asset_offline_description": "ഈ പുറത്തുള്ള ശേഖരത്തിലെ വസ്തുക്കള്‍ ഇനി ഡിസ്കിൽ കാണുന്നില്ല, അവയെ ട്രാഷിലേക്ക് നീക്കിയിരിക്കുന്നു. ഫയൽ ലൈബ്രറിക്കുള്ളിൽ നിന്ന് നീക്കിയിട്ടുണ്ടെങ്കിൽ, പുതിയ അനുബന്ധ വസ്തുവിനായി നിങ്ങളുടെ സമയക്രമം (ടൈംലൈന്‍) പരിശോധിക്കുക. ഈ വസ്തു പുനഃസ്ഥാപിക്കാൻ, താഴെയുള്ള ഫയൽ പാത്ത് ഇമ്മിച്ചിന് എത്തിപ്പെടാന്‍ കഴിയുമെന്ന് ഉറപ്പാക്കുകയും ശേഖരം പുനഃപരിശോധിക്കുകയും (സ്കാൻ) ചെയ്യുക.", + "authentication_settings": "ആധികാരികതാ സജ്ജീകരണങ്ങൾ", + "authentication_settings_description": "പാസ്സ്‌വേര്‍ഡ്‌, OAuth തുടങ്ങിയ സജ്ജീകരണങ്ങള്‍", + "authentication_settings_disable_all": "എല്ലാ പ്രവേശന (ലോഗിൻ) രീതികളും പ്രവർത്തനരഹിതമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? പ്രവേശനങ്ങള്‍ പൂർണ്ണമായും പ്രവർത്തനരഹിതമാക്കപ്പെടും.", + "background_task_job": "പശ്ചാത്തല പ്രവര്‍ത്തികള്‍", + "backup_database": "ഡാറ്റാബേസ് ഡംമ്പ് ഉണ്ടാക്കുക", + "backup_database_enable_description": "ഡാറ്റാബേസ് ഡമ്പുകൾ പ്രാപ്തമാക്കുക", + "backup_keep_last_amount": "പഴയ ഡാറ്റാബേസ് ഡമ്പുകൾ എത്രയെണ്ണം സൂക്ഷിക്കണം", + "backup_settings": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങള്‍", + "backup_settings_description": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക.", + "cleared_jobs": "{job} - ന്‍റെ ജോലികള്‍ മായ്ച്ചിരിക്കുന്നു", + "config_set_by_file": "ക്രമീകരണങ്ങള്‍ ഇപ്പോള്‍ ഒരു ക്രമീകരണ ഫയല്‍ വഴിയാണ് നിശ്ചയിക്കുന്നത്", + "confirm_delete_library": "{library} മായ്ച്ചു കളയണം എന്നുറപ്പാണോ?", + "confirm_delete_library_assets": "ഈ ശേഖരം ഇല്ലാതാക്കണം എന്ന് ഉറപ്പാണോ? ഇത് ഇമ്മിച്ചിൽ നിന്ന് {count, plural, one {# contained asset} other {all # contained assets}} ഇല്ലാതാക്കും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല. ഫയലുകൾ ഡിസ്കിൽ തന്നെ തുടരും.", + "confirm_email_below": "തീര്‍ച്ചപ്പെടുത്താന്‍ {email} താഴെ കൊടുക്കുക", + "confirm_reprocess_all_faces": "എല്ലാ മുഖങ്ങളും വീണ്ടും കണ്ടെത്തണം എന്ന് ഉറപ്പാണോ? ഇത് ഇതിനകം പേരു ചേര്‍ത്ത മുഖങ്ങളെയും ആളുകളെയും മായ്ക്കും.", + "confirm_user_password_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ പാസ്സ്‌വേര്‍ഡ്‌ പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "confirm_user_pin_code_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ PIN പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "create_job": "ജോലി സൃഷ്ടിക്കുക", + "cron_expression": "ക്രോണ്‍ (cron) പ്രയോഗശൈലി", + "cron_expression_description": "ക്രോൺ ഫോർമാറ്റ് ഉപയോഗിച്ച് സ്കാനിംഗ് ഇടവേള സജ്ജമാക്കുക. കൂടുതൽ വിവരങ്ങൾക്ക് Crontab Guru സന്ദര്‍ശിക്കുക", + "cron_expression_presets": "മുന്‍കൂട്ടി തയ്യാര്‍ ചെയ്ത ക്രോണ്‍ പ്രവര്‍ത്തനശൈലികള്‍", + "disable_login": "ലോഗിന്‍ തടയുക", + "duplicate_detection_job_description": "സമാനമായ ചിത്രങ്ങൾ കണ്ടെത്താൻ വസ്തുവഹകളില്‍ യന്ത്രപഠനം പ്രവർത്തിപ്പിക്കുക. ഇത് സ്മാർട്ട് സര്‍ച്ചിനെ ആശ്രയിക്കുന്നു", + "exclusion_pattern_description": "നിങ്ങളുടെ ലൈബ്രറി സ്കാൻ ചെയ്യുമ്പോൾ ഫയലുകളും ഫോൾഡറുകളും അവഗണിക്കാൻ ഒഴിവാക്കല്‍ മാതൃകകള്‍ നിങ്ങളെ അനുവദിക്കുന്നു. RAW ഫയലുകൾ പോലുള്ള നിങ്ങൾക്ക് ഇറക്കുമതി ചെയ്യാൻ താൽപ്പര്യമില്ലാത്ത ഫയലുകൾ അടങ്ങിയ ഫോൾഡറുകൾ ഉണ്ടെങ്കിൽ ഇത് ഉപയോഗപ്രദമാണ്.", + "external_library_management": "ബാഹ്യമായശേഖരങ്ങളുടെ നിയന്ത്രണം", + "face_detection": "മുഖങ്ങള്‍ കണ്ടെത്തുക", + "face_detection_description": "യന്ത്രപഠനം ഉപയോഗിച്ച് വസ്തുക്കളിലെ മുഖങ്ങൾ കണ്ടെത്തുക. വീഡിയോകൾക്ക്, തംബ്‌നെയിൽ മാത്രമേ പരിഗണിക്കൂ. \"Refresh\" എല്ലാ വസ്തുക്കളും (വീണ്ടും) പ്രോസസ്സ് ചെയ്യുന്നു. കൂടാതെ \"Reset\" നിലവിലുള്ള എല്ലാ മുഖളും വിവരങ്ങളും മായ്‌ക്കുന്നു. \"Missing\" ഇതുവരെ ക്രമീകരിക്കാത്ത വസ്തുക്കളെ വരിയിലേക്ക് നിർത്തുന്നു. മുഖം തിരിച്ചറിയൽ പൂർത്തിയായ ശേഷം കണ്ടെത്തിയ മുഖങ്ങൾ മുഖം തിരിച്ചറിയലിനായി ക്യൂവിൽ നിർത്തും, അവയെ നിലവിലുള്ളതോ പുതിയതോ ആയ ആളുകളിലേക്ക് ഗ്രൂപ്പുചെയ്യും.", + "facial_recognition_job_description": "കണ്ടെത്തിയ മുഖങ്ങളെ ആളുകളുടെ കൂട്ടം ആക്കുക. മുഖം കണ്ടെത്തല്‍ ഘട്ടത്തിനു ശേഷമേ ഇത് ഉണ്ടാകൂ. \"Reset\" വീണ്ടും കൂട്ടങ്ങളെ ഉണ്ടാക്കും. \"Missing\" ആളെ നിയോഗിക്കാത്ത മുഖങ്ങളെ വരിയിലേക്ക് ചേര്‍ക്കുന്നു.", + "failed_job_command": "{job} എന്ന ജോലിക്ക് വേണ്ടിയുള്ള ആജ്ഞ {command} പരാജയപ്പെട്ടിരിക്കുന്നു", + "force_delete_user_warning": "മുന്നറിയിപ്പ്: ഇത് ഉപയോക്താവിനെയും എല്ലാ വസ്തുക്കളേയും ഉടനടി നീക്കം ചെയ്യും. ഇത് പഴയപടിയാക്കാനോ ഫയലുകൾ വീണ്ടെടുക്കാനോ കഴിയില്ല.", + "image_format": "ഘടന", + "image_format_description": "WebP ഉണ്ടാക്കാന്‍ സമയം എടുക്കും എങ്കിലും JPEG ഫയലുകളെക്കാള്‍ ചെറുതായിരിക്കും.", + "image_fullsize_description": "അധികവിവരങ്ങള്‍ ഒഴിവാക്കിയ ചിത്രം, വലുതാക്കി കാണിക്കുമ്പോള്‍ ഉപയോഗിക്കപ്പെടുന്നു", + "image_fullsize_enabled": "പൂര്‍ണ വലുപ്പത്തില്‍ ഉള്ള ചിത്രങ്ങള്‍ ഉണ്ടാക്കാന്‍പ്രാപ്തമാക്കുക" + } } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 2abc08e7f2..1d8a3455e7 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -14,6 +14,7 @@ "add_a_location": "Legg til sted", "add_a_name": "Legg til navn", "add_a_title": "Legg til tittel", + "add_birthday": "Legg til bursdag", "add_endpoint": "API endepunkt", "add_exclusion_pattern": "Legg til ekskluderingsmønster", "add_import_path": "Legg til importsti", @@ -44,16 +45,23 @@ "backup_database": "Opprett database-dump", "backup_database_enable_description": "Aktiver database-dump", "backup_keep_last_amount": "Antall database-dumps å beholde", + "backup_onboarding_1_description": "ekstern kopi i skyen eller på et annet fysisk sted.", + "backup_onboarding_2_description": "lokale kopier på forsskjellige enheter. Dette inkluderer hovedfilene og en lokal sikkerhetskopi av disse filene.", + "backup_onboarding_3_description": "totale kopier av dataene dine, inkludert originalfilene. Dette inkluderer én ekstern kopi og to lokale kopier.", + "backup_onboarding_description": "En 3-2-1 sikkerhetskopieringsstrategi anbefales for å beskytte dataene dine. Du bør beholde kopier av opplastede bilder/videoer samt Immich-databasen for en omfattende sikkerhetskopieringsløsning.", + "backup_onboarding_footer": "For mer informasjon om sikkerhetskopiering av Immich, se dokumentasjonen.", + "backup_onboarding_parts_title": "En 3-2-1 sikkerhetskopi inkluderer:", + "backup_onboarding_title": "Sikkerhetskopier", "backup_settings": "Database-dump instillinger", "backup_settings_description": "Håndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", "confirm_delete_library": "Er du sikker på at du vil slette biblioteket {library}?", - "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alle {count, plural, one {# contained asset} other {all # contained assets}} tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", + "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# objekt} other {# objekter}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", "confirm_email_below": "For å bekrefte, skriv inn \"{email}\" nedenfor", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikter på nytt? Dette vil også fjerne navngitte personer.", "confirm_user_password_reset": "Er du sikker på at du vil tilbakestille passordet til {user}?", - "confirm_user_pin_code_reset": "Er du sikker på at du vil resette {user}'s PIN kode?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille PIN-koden til {user} ?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -397,6 +405,7 @@ "album_cover_updated": "Albumomslag oppdatert", "album_delete_confirmation": "Er du sikker på at du vil slette albumet {album}?", "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", + "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERT", "album_info_card_backup_album_included": "INKLUDERT", "album_info_updated": "Albuminformasjon oppdatert", @@ -483,25 +492,25 @@ "asset_viewer_settings_subtitle": "Endre dine visningsinnstillinger for galleriet", "asset_viewer_settings_title": "Objektviser", "assets": "Filer", - "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", - "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", + "assets_added_count": "Lagt til {count, plural, one {# objekt} other {# objekter}}", + "assets_added_to_album_count": "Lagt til {count, plural, one {# objekter} other {# objekt}} i album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", - "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", - "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", + "assets_moved_to_trash_count": "Flyttet {count, plural, one {# objekt} other {# objekter}} til søppel", + "assets_permanently_deleted_count": "Slettet {count, plural, one {# objekt} other {# objekter}} permanent", + "assets_removed_count": "Slettet {count, plural, one {# objekt} other {# objekter}}", "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", "assets_restore_confirmation": "Er du sikker på at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! Vær oppmerksom på at frakoblede ressurser ikke kan gjenopprettes på denne måten.", - "assets_restored_count": "Gjenopprettet {count, plural, one {# asset} other {# assets}}", + "assets_restored_count": "Gjenopprettet {count, plural, one {# objekt} other {# objekter}}", "assets_restored_successfully": "{count} objekt(er) gjenopprettet", "assets_trashed": "{count} objekt(er) slettet", - "assets_trashed_count": "Kastet {count, plural, one {# asset} other {# assets}}", + "assets_trashed_count": "Kastet {count, plural, one {# objekt} other {# objekter}}", "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", - "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} er allerede lagt til i albumet", + "assets_were_part_of_album_count": "{count, plural, one {Objektet} other {Objektene}} er allerede lagt til i albumet", "authorized_devices": "Autoriserte enheter", "automatic_endpoint_switching_subtitle": "Koble til lokalt over angitt Wi-Fi når det er tilgjengelig, og bruk alternative tilkoblinger andre steder", "automatic_endpoint_switching_title": "Automatisk URL bytte", @@ -510,6 +519,7 @@ "back_close_deselect": "Tilbake, lukk eller fjern merking", "background_location_permission": "Bakgrunnstillatelse for plassering", "background_location_permission_content": "For å bytte nettverk når du kjører i bakgrunnen, må Immich *alltid* ha presis posisjonstilgang slik at appen kan lese Wi-Fi-nettverkets navn", + "backup": "Sikkerhetskopiering", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Trykk for å inkludere, dobbelttrykk for å ekskludere", "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", @@ -541,7 +551,7 @@ "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", "backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten", "backup_controller_page_background_wifi": "Kun på Wi-Fi", - "backup_controller_page_backup": "Sikkerhetskopier", + "backup_controller_page_backup": "Sikkerhetskopiere", "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {date}", @@ -585,9 +595,9 @@ "bugs_and_feature_requests": "Feil og funksjonsforespørsler", "build": "Bygg", "build_image": "Lag Bilde", - "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", - "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil løse alle dupliserte grupper uten å slette noe.", - "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", + "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", + "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten å slette noe.", + "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", @@ -723,6 +733,7 @@ "current_server_address": "Nåværende serveradresse", "custom_locale": "Tilpasset lokalisering", "custom_locale_description": "Formater datoer og tall basert på språk og region", + "custom_url": "Tilpasset URL", "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", @@ -742,7 +753,8 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", - "delete_action_prompt": "{count} permanen slettet", + "delete_action_confirmation_message": "Er du sikker på at du vil slette dette objektet? Dette vil flytte objektet til søppelkassen og vil gi deg beskjed om du vil slette det lokalt", + "delete_action_prompt": "{count} slettet", "delete_album": "Slett album", "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", + "delete_permanently": "Slett permanent", + "delete_permanently_action_prompt": "{count} slettet permanent", "delete_shared_link": "Slett delt lenke", "delete_shared_link_dialog_title": "Slett delt link", "delete_tag": "Slett tag", @@ -815,6 +829,7 @@ "edit": "Rediger", "edit_album": "Rediger album", "edit_avatar": "Rediger avatar", + "edit_birthday": "Rediger Bursdag", "edit_date": "Rediger dato", "edit_date_and_time": "Rediger dato og tid", "edit_description": "Endre beskrivelse", @@ -866,7 +881,7 @@ "cant_apply_changes": "Kan ikke legge til endringene", "cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet", "cant_change_asset_favorite": "Kan ikke endre favoritt til filen", - "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# objekt} other {# objekter}}", "cant_get_faces": "Kan ikke finne ansikter", "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", "cant_search_people": "Kan ikke søke etter mennesker", @@ -903,8 +918,8 @@ "unable_to_add_exclusion_pattern": "Kan ikke legge til eksklusjonsmønster", "unable_to_add_import_path": "Kan ikke legge til importsti", "unable_to_add_partners": "Kan ikke legge til partnere", - "unable_to_add_remove_archive": "Kan ikke {archived, select, true {remove asset from} other {add asset to}} arkivet", - "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {add asset to} other {remove asset from}} favoritter", + "unable_to_add_remove_archive": "Kan ikke {archived, select, true {fjerne objekt fra} other {flytte objekt til}} arkivet", + "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {legge til objekt til} other {fjerne objekt fra}} favoritter", "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", "unable_to_change_date": "Kan ikke endre dato", @@ -982,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Legg til beskrivelse ...", + "exif_bottom_sheet_description_error": "Feil ved oppdatering av beskrivelsen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", @@ -1002,6 +1018,8 @@ "explorer": "Utforsker", "export": "Eksporter", "export_as_json": "Eksporter som JSON", + "export_database": "Eksporter database", + "export_database_description": "Eksporter SQLite databasen", "extension": "Utvidelse", "external": "Ekstern", "external_libraries": "Eksterne Bibliotek", @@ -1139,13 +1157,14 @@ "keep": "Behold", "keep_all": "Behold alle", "keep_this_delete_others": "Behold denne, slett de andre", - "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# asset} other {# assets}}", + "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# objekt} other {# objekter}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "Språk", "language_no_results_subtitle": "Prøv å endre søkeord", "language_no_results_title": "Ingen språk funnet", "language_search_hint": "Søker etter språk...", - "language_setting_description": "Velg ditt foretrukket språk", + "language_setting_description": "Velg ditt foretrukne språk", + "large_files": "Store Filer", "last_seen": "Sist sett", "latest_version": "Siste versjon", "latitude": "Breddegrad", @@ -1165,7 +1184,6 @@ "light": "Lys", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", - "link_options": "Lenkealternativer", "link_to_oauth": "Lenke til OAuth", "linked_oauth_account": "Lenket til OAuth-konto", "list": "Liste", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Administrer dine innloggede enheter", "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", - "map_assets_in_bound": "{count} bilde", - "map_assets_in_bounds": "{count} bilder", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", @@ -1286,8 +1303,8 @@ "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", - "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", - "moved_to_library": "Flyttet {count, plural, one {# asset} other {# assets}} til biblioteket", + "moved_to_archive": "Flyttet {count, plural, one {# objekt} other {# objekter}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# objekt} other {# objekter}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato på objekt(er) med kun lese-rettigheter, hopper over", "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon på objekt(er) med kun lese-rettigheter, hopper over", @@ -1408,10 +1425,10 @@ "permanent_deletion_warning": "Advarsel om permanent sletting", "permanent_deletion_warning_setting_description": "Vis en advarsel ved permanent sletting av filer", "permanently_delete": "Slett permanent", - "permanently_delete_assets_count": "Permanent slett {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {this asset?} other {these # assets?}} Dette vil også slette {count, plural, one {it from its} other {them from their}} album.", + "permanently_delete_assets_count": "Slett {count, plural, one {objekt} other {objekter}} permanent", + "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {dette objektet?} other {disse # objektene?}} Dette vil også slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", "permanently_deleted_asset": "Filen har blitt permanent slettet", - "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# asset} other {# assets}}", + "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# objekt} other {# objekter}}", "permission": "Tillatelse", "permission_empty": "Dine tillatelser burde ikke være tomme", "permission_onboarding_back": "Tilbake", @@ -1508,8 +1525,8 @@ "reaction_options": "Reaksjonsalternativer", "read_changelog": "Les endringslogg", "reassign": "Tilordne på nytt", - "reassigned_assets_to_existing_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", - "reassigned_assets_to_new_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} til en ny person", + "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} to {name, select, null {en eksisterende person} other {{name}}}", + "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} til en ny person", "reassing_hint": "Tilordne valgte eiendeler til en eksisterende person", "recent": "Nylig", "recent-albums": "Nylige album", @@ -1532,8 +1549,8 @@ "remote": "Eksternt", "remote_assets": "Eksterne objekter", "remove": "Fjern", - "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", - "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", + "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# objekt} other {# objekter}} fra albumet?", + "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# objekt} other {# objekter}} fra den delte lenken?", "remove_assets_title": "Vil du fjerne eiendeler?", "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", @@ -1555,7 +1572,7 @@ "removed_from_favorites_count": "{count, plural, other {Removed #}} fra favoritter", "removed_memory": "Slettet minne", "removed_photo_from_memory": "Slettet bilde fra minne", - "removed_tagged_assets": "Fjern tag fra {count, plural, one {# asset} other {# assets}}", + "removed_tagged_assets": "Fjern tag fra {count, plural, one {# objekt} other {# objekter}}", "rename": "Gi nytt navn", "repair": "Reparer", "repair_no_results_message": "Usporrede og savnede filer vil vises her", @@ -1582,6 +1599,7 @@ "resume": "Fortsett", "retry_upload": "Prøv opplasting på nytt", "review_duplicates": "Gjennomgå duplikater", + "review_large_files": "Se gjennom store filer", "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopiert til utklippslisten", "shared_link_clipboard_text": "Link: {link}\nPassord: {password}", "shared_link_create_error": "Feil ved oppretting av delbar link", + "shared_link_custom_url_description": "Få tilgang til denne delte lenken med en egendefinert URL", "shared_link_edit_description_hint": "Endre delebeskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dager", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Håndter delte linker", "shared_link_options": "Alternativer for delte lenke", + "shared_link_password_description": "Krev et passord for å få tilgang til denne delte lenken", "shared_links": "Delte linker", "shared_links_description": "Del bilder og videoer med lenke", "shared_photos_and_videos_count": "{assetCount, plural, other {# delte bilder og videoer.}}", @@ -1823,7 +1843,7 @@ "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", - "stacked_assets_count": "Stable {count, plural, one {# asset} other {# assets}}", + "stacked_assets_count": "Stable {count, plural, one {# objekt} other {# objekter}}", "stacktrace": "Stakkspor", "start": "Start", "start_date": "Startdato", @@ -1859,7 +1879,7 @@ "tag_not_found_question": "Finner du ikke en merke? Opprett en nytt merke.", "tag_people": "Tag Folk", "tag_updated": "Oppdater merke: {tag}", - "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", + "tagged_assets": "Merket {count, plural, one {# objekt} other {# objekter}}", "tags": "Merker", "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", @@ -1935,25 +1955,28 @@ "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", "unstack_action_prompt": "{count} ustakket", - "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "unstacked_assets_count": "Ikke stablet {count, plural, one {# objekt} other {# objekter}}", "untagged": "Umerket", "up_next": "Neste", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", + "upload_action_prompt": "{count} i kø for opplasting", "upload_concurrency": "Samtidig opplastning", "upload_details": "Opplastingsdetaljer", "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", "upload_dialog_title": "Last opp objekt", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", + "upload_finished": "Opplasting fullført", "upload_progress": "Gjenstående {remaining, number} – behandlet {processed, number}/{total, number}", - "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplicate asset} other {# duplicate assets}}", + "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}", "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", "upload_success": "Opplasting vellykket, oppdater siden for å se nye opplastninger.", "upload_to_immich": "Last opp til Immich ({count})", "uploading": "Laster opp", + "uploading_media": "Laster opp media", "url": "URL", "usage": "Bruk", "use_biometric": "Bruk biometri", @@ -1962,7 +1985,7 @@ "user": "Bruker", "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", - "user_liked": "{user} likte {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette objektet} other {dette}}", "user_pin_code_settings": "PINkode", "user_pin_code_settings_description": "Håndter din PINkode", "user_privacy": "Personverninnstillinger", @@ -1974,7 +1997,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", - "users_added_to_album_count": "Lagt til {count, plural, one {#user} other {#users}} til albumet", + "users_added_to_album_count": "Lagt til {count, plural, one {# bruker} other {# brukere}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", diff --git a/i18n/nl.json b/i18n/nl.json index 86cb0fba6e..b287a7810f 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -14,6 +14,7 @@ "add_a_location": "Een locatie toevoegen", "add_a_name": "Naam toevoegen", "add_a_title": "Titel toevoegen", + "add_birthday": "Voeg een verjaardag toe", "add_endpoint": "Server toevoegen", "add_exclusion_pattern": "Uitsluitingspatroon toevoegen", "add_import_path": "Import-pad toevoegen", @@ -44,8 +45,15 @@ "backup_database": "Maak database back-up", "backup_database_enable_description": "Database back-ups activeren", "backup_keep_last_amount": "Aantal back-ups om te bewaren", - "backup_settings": "Database back-up instellingen", - "backup_settings_description": "Beheer database back-up instellingen.", + "backup_onboarding_1_description": "externe kopie in de cloud of op een andere fysieke locatie.", + "backup_onboarding_2_description": "lokale kopieën op verschillende apparaten. Dit omvat de hoofdbestanden én een lokale back-up van deze bestanden.", + "backup_onboarding_3_description": "totaal aantal kopieën van de gegevens, inclusief originele bestanden. Dit omvat 1 externe kopie en 2 lokale kopieën.", + "backup_onboarding_description": "Een 3-2-1 back-up strategie wordt aanbevolen om de gegevens te beschermen. Bewaar kopieën van de geüploade foto's/video's en de Immich database voor een complete back-up oplossing.", + "backup_onboarding_footer": "Raadpleeg de documentatie voor meer informatie over het maken van back-ups van Immich.", + "backup_onboarding_parts_title": "Een 3-2-1 back-up omvat:", + "backup_onboarding_title": "Back-ups", + "backup_settings": "Database dump instellingen", + "backup_settings_description": "Beheer database dump instellingen.", "cleared_jobs": "Taken gewist voor: {job}", "config_set_by_file": "Instellingen worden momenteel beheerd door een configuratiebestand", "confirm_delete_library": "Weet je zeker dat je de bibliotheek {library} wilt verwijderen?", @@ -397,6 +405,7 @@ "album_cover_updated": "Album cover is bijgewerkt", "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_deleted": "Album verwijderd", "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INBEGREPEN", "album_info_updated": "Albumgegevens bijgewerkt", @@ -447,7 +456,7 @@ "app_settings": "App instellingen", "appears_in": "Komt voor in", "archive": "Archief", - "archive_action_prompt": "{count}toegevoegd aan Archief", + "archive_action_prompt": "{count} toegevoegd aan archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", "archive_page_title": "Archief ({count})", @@ -471,7 +480,7 @@ "asset_list_layout_settings_group_by": "Groepeer assets per", "asset_list_layout_settings_group_by_month_day": "Maand + dag", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Fotorasterlayoutinstellingen", + "asset_list_settings_subtitle": "Fotoraster layout instellingen", "asset_list_settings_title": "Fotoraster", "asset_offline": "Asset offline", "asset_offline_description": "Deze externe asset is niet meer op de schijf te vinden. Neem contact op met de Immich beheerder voor hulp.", @@ -510,34 +519,35 @@ "back_close_deselect": "Terug, sluiten of deselecteren", "background_location_permission": "Achtergrond locatie toestemming", "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het WiFi-netwerk te kunnen lezen", + "backup": "Back-up", "backup_album_selection_page_albums_device": "Albums op apparaat ({count})", - "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", - "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.", - "backup_album_selection_page_select_albums": "Albums selecteren", + "backup_album_selection_page_albums_tap": "Tik om op te nemen, dubbel tik om uit te sluiten", + "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het back-up proces.", + "backup_album_selection_page_select_albums": "Selecteer albums", "backup_album_selection_page_selection_info": "Selectie info", "backup_album_selection_page_total_assets": "Totaal unieke assets", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Fout bij back-uppen assets. Opnieuw proberen…", - "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberen…", - "backup_background_service_current_upload_notification": "{filename} aan het uploaden...", + "backup_background_service_backup_failed_message": "Fout bij het back-uppen van de assets. Opnieuw proberen…", + "backup_background_service_connection_failed_message": "Fout bij het verbinden met de server. Opnieuw proberen…", + "backup_background_service_current_upload_notification": "{filename} wordt geüpload...", "backup_background_service_default_notification": "Controleren op nieuwe assets…", - "backup_background_service_error_title": "Backupfout", + "backup_background_service_error_title": "Back-up fout", "backup_background_service_in_progress_notification": "Back-up van assets maken…", "backup_background_service_upload_failure_notification": "Fout bij het uploaden van {filename}", "backup_controller_page_albums": "Back-up albums", - "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via Instellingen > Algemeen > Ververs op achtergrond, om back-ups op de achtergrond te maken.", - "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld", + "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via 'Instellingen > Algemeen > Ververs op achtergrond', om back-ups op de achtergrond te maken.", + "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond is uitgeschakeld", "backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen", "backup_controller_page_background_battery_info_link": "Laat zien hoe", - "backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", + "backup_controller_page_background_battery_info_message": "Voor de beste achtergrond back-up ervaring schakelt u alle batterij optimalisaties uit die de achtergrondactiviteit voor Immich kunnen beperken.\n\nAangezien dit apparaat specifiek is, raden we aan om de vereiste informatie op te zoeken bij de fabrikant van je apparaat.", "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Batterijoptimalisaties", + "backup_controller_page_background_battery_info_title": "Batterij optimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", - "backup_controller_page_background_configure_error": "Achtergrondserviceconfiguratie mislukt", - "backup_controller_page_background_delay": "Back-upvertraging voor nieuwe assets: {duration}", + "backup_controller_page_background_configure_error": "Achtergrondservice configuratie mislukt", + "backup_controller_page_background_delay": "Back-up vertraging voor nieuwe assets: {duration}", "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe assets zonder de app te hoeven openen", - "backup_controller_page_background_is_off": "Automatische achtergrondback-up staat uit", - "backup_controller_page_background_is_on": "Automatische achtergrondback-up staat aan", + "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", "backup_controller_page_background_turn_off": "Achtergrondservice uitzetten", "backup_controller_page_background_turn_on": "Achtergrondservice aanzetten", "backup_controller_page_background_wifi": "Alleen op WiFi", @@ -723,6 +733,7 @@ "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", + "custom_url": "Aangepaste URL", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", @@ -738,11 +749,12 @@ "deduplication_criteria_1": "Grootte van afbeelding in bytes", "deduplication_criteria_2": "Aantal EXIF data", "deduplication_info": "Deduplicatie-info", - "deduplication_info_description": "Om automatisch bezittingen te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", + "deduplication_info_description": "Om automatisch items te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", - "delete_action_prompt": "{count} permanent verwijderd", + "delete_action_confirmation_message": "Weet je zeker dat je dit item wilt verwijderen? Deze actie zorgt ervoor dat het item naar de prullenbak van de server wordt verplaatst en je wordt gevraagd of je deze ook lokaal wilt verwijderen", + "delete_action_prompt": "{count} verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", + "delete_permanently": "Permanent verwijderen", + "delete_permanently_action_prompt": "{count} permanent verwijderd", "delete_shared_link": "Verwijder gedeelde link", "delete_shared_link_dialog_title": "Verwijder gedeelde link", "delete_tag": "Tag verwijderen", @@ -770,7 +784,7 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", - "deselect_all": "Alles Deselecteren", + "deselect_all": "Alles deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", @@ -815,6 +829,7 @@ "edit": "Bewerken", "edit_album": "Album bewerken", "edit_avatar": "Avatar bewerken", + "edit_birthday": "Wijzig verjaardag", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", "edit_description": "Beschrijving bewerken", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Beschrijving toevoegen...", + "exif_bottom_sheet_description_error": "Fout bij het bijwerken van de beschrijving", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", "exif_bottom_sheet_people": "MENSEN", @@ -1002,6 +1018,8 @@ "explorer": "Verkenner", "export": "Exporteren", "export_as_json": "Exporteren als JSON", + "export_database": "Exporteer database", + "export_database_description": "Exporteer de SQLite database", "extension": "Extensie", "external": "Extern", "external_libraries": "Externe bibliotheken", @@ -1088,7 +1106,7 @@ "host": "Host", "hour": "Uur", "id": "ID", - "idle": "Idle", + "idle": "Inactief", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Geen talen gevonden", "language_search_hint": "Zoek talen...", "language_setting_description": "Selecteer je voorkeurstaal", + "large_files": "Grote bestanden", "last_seen": "Laatst gezien", "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", @@ -1165,7 +1184,6 @@ "light": "Licht", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", - "link_options": "Opties voor koppeling", "link_to_oauth": "Koppel OAuth", "linked_oauth_account": "Gekoppeld OAuth account", "list": "Lijst", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Beheer je ingelogde apparaten", "manage_your_oauth_connection": "Beheer je OAuth-koppeling", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto's", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto's}}", "map_cannot_get_user_location": "Kan locatie van de gebruiker niet ophalen", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Gebruik deze locatie", @@ -1529,8 +1546,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", - "remote": "Remote", - "remote_assets": "Remote Assets", + "remote": "Externe", + "remote_assets": "Externe Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1568,8 +1585,8 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", - "reset_sqlite": "Reset SQLite Database", - "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moetenn uitloggen om de data opnieuw te synchroniseren.", + "reset_sqlite": "SQLite database resetten", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moeten uitloggen om de data opnieuw te synchroniseren", "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", @@ -1582,6 +1599,7 @@ "resume": "Hervatten", "retry_upload": "Opnieuw uploaden", "review_duplicates": "Controleer duplicaten", + "review_large_files": "Grote bestanden beoordelen", "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Gekopieerd naar klembord", "shared_link_clipboard_text": "Link: {link}\nWachtwoord: {password}", "shared_link_create_error": "Fout bij het maken van een gedeelde link", + "shared_link_custom_url_description": "Krijg toegang tot deze gedeelde link met een aangepaste URL", "shared_link_edit_description_hint": "Voer beschrijving voor de gedeelde link in", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dagen", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Beheer gedeelde links", "shared_link_options": "Opties voor gedeelde links", + "shared_link_password_description": "Vraag een wachtwoord om toegang te krijgen tot deze gedeelde link", "shared_links": "Gedeelde links", "shared_links_description": "Deel foto's en video's via een link", "shared_photos_and_videos_count": "{assetCount, plural, other {# gedeelde foto's & video's.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Geüpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", + "upload_action_prompt": "{count} in de wachtrij voor uploaden", "upload_concurrency": "Aantal gelijktijdige uploads", "upload_details": "Uploaddetails", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", + "upload_finished": "Uploaden is voltooid", "upload_progress": "Resterend {remaining, number} - Verwerkt {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# duplicate asset} other {# duplicate assets}} overgeslagen", "upload_status_duplicates": "Duplicaten", @@ -1954,6 +1976,7 @@ "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", "upload_to_immich": "Uploaden naar Immich ({count})", "uploading": "Aan het uploaden", + "uploading_media": "Media wordt geüpload", "url": "URL", "usage": "Gebruik", "use_biometric": "Gebruik biometrische authenticatie", diff --git a/i18n/pl.json b/i18n/pl.json index 5adc6df943..3c6a8b12e0 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -397,6 +397,7 @@ "album_cover_updated": "Okładka albumu została zaktualizowana", "album_delete_confirmation": "Czy na pewno chcesz usunąć album {album}?", "album_delete_confirmation_description": "Jeżeli album jest udostępniany, inny stracą do niego dostęp.", + "album_deleted": "Album usunięty", "album_info_card_backup_album_excluded": "WYKLUCZONE", "album_info_card_backup_album_included": "WŁĄCZONE", "album_info_updated": "Szczegóły albumu zostały zaktualizowane", @@ -406,6 +407,7 @@ "album_options": "Opcje albumu", "album_remove_user": "Usunąć użytkownika?", "album_remove_user_confirmation": "Na pewno chcesz usunąć {user}?", + "album_search_not_found": "Nie znaleziono albumów pasujących do Twojego wyszukiwania", "album_share_no_users": "Wygląda na to, że ten album albo udostępniono wszystkim użytkownikom, albo nie ma komu go udostępnić.", "album_updated": "Album zaktualizowany", "album_updated_setting_description": "Otrzymaj powiadomienie e-mail, gdy do udostępnionego Ci albumu zostaną dodane nowe zasoby", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Domyślna kolejność sortowania w albumach", "albums_default_sort_order_description": "Początkowa kolejność sortowania zasobów przy tworzeniu nowych albumów.", "albums_feature_description": "Kolekcje zasobów, które można udostępniać innym użytkownikom.", + "albums_on_device_count": "Albumów na urzadzeniu ({count})", "all": "Wszystkie", "all_albums": "Wszystkie albumy", "all_people": "Wszystkie osoby", @@ -508,6 +511,7 @@ "back_close_deselect": "Wróć, zamknij lub odznacz", "background_location_permission": "Uprawnienia do lokalizacji w tle", "background_location_permission_content": "Aby móc przełączać sieć podczas pracy w tle, Immich musi *zawsze* mieć dostęp do dokładnej lokalizacji, aby aplikacja mogła odczytać nazwę sieci Wi-Fi", + "backup": "Kopia Zapasowa", "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({count})", "backup_album_selection_page_albums_tap": "Stuknij, aby włączyć, stuknij dwukrotnie, aby wykluczyć", "backup_album_selection_page_assets_scatter": "Pliki mogą być rozproszone w wielu albumach. Dzięki temu albumy mogą być włączane lub wyłączane podczas procesu tworzenia kopii zapasowej.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opcje kopi zapasowej", "backup_setting_subtitle": "Zarządzaj ustawieniami przesyłania w tle i na pierwszym planie", "backward": "Do tyłu", + "beta_sync": "Status synchronizacji w wersji Beta", + "beta_sync_subtitle": "Zarządzaj nowym systemem synchronizacji", "biometric_auth_enabled": "Włączono logowanie biometryczne", "biometric_locked_out": "Uwierzytelnianie biometryczne jest dla Ciebie zablokowane", "biometric_no_options": "Brak możliwości biometrii", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Wyczyść Cache", "cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopóki pamięć podręczna nie zostanie odbudowana.", "cache_settings_duplicated_assets_clear_button": "WYCZYŚĆ", - "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na czarnej liście aplikacji", + "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na liście ignorowanych w aplikacji", "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({count})", "cache_settings_statistics_album": "Biblioteka miniatur", "cache_settings_statistics_full": "Pełne Zdjęcia", @@ -605,6 +611,7 @@ "cancel": "Anuluj", "cancel_search": "Anuluj wyszukiwanie", "canceled": "Anulowano", + "canceling": "Anulowanie", "cannot_merge_people": "Złączenie osób nie powiodło się", "cannot_undo_this_action": "Nie da się tego cofnąć!", "cannot_update_the_description": "Nie można zaktualizować opisu", @@ -718,6 +725,7 @@ "current_server_address": "Aktualny adres serwera", "custom_locale": "Niestandardowy Region", "custom_locale_description": "Formatuj daty i liczby na podstawie języka i regionu", + "custom_url": "Niestandardowy URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", @@ -737,7 +745,8 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", - "delete_action_prompt": "{count} trwale usuniętych", + "delete_action_confirmation_message": "Jesteś pewien, że chcesz usunąć ten zasób? Ta czynność przeniesie zasób do kosza na serwerze i wyświetli komunikat z pytaniem, czy chcesz go usunąć lokalnie", + "delete_action_prompt": "{count} usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", @@ -755,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", "delete_others": "Usuń inne", + "delete_permanently": "Usuń trwale", + "delete_permanently_action_prompt": "{count} trwale usuniętych", "delete_shared_link": "Usuń udostępniony link", "delete_shared_link_dialog_title": "Usuń udostępniony link", "delete_tag": "Usuń etykietę", @@ -765,6 +776,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Błąd aktualizacji opisu, sprawdź dziennik, aby uzyskać więcej szczegółów", + "deselect_all": "Odznacz wszystkie", "details": "Szczegóły", "direction": "Kierunek", "disabled": "Wyłączone", @@ -782,6 +794,7 @@ "documentation": "Dokumentacja", "done": "Gotowe", "download": "Pobierz", + "download_action_prompt": "Pobieranie {count} zasobów", "download_canceled": "Pobieranie anulowane", "download_complete": "Pobieranie zakończone", "download_enqueue": "Pobieranie w kolejce", @@ -838,6 +851,7 @@ "empty_trash": "Opróżnij kosz", "empty_trash_confirmation": "Czy na pewno chcesz opróżnić kosz? Spowoduje to trwałe usunięcie wszystkich zasobów znajdujących się w koszu z Immich.\nNie można cofnąć tej operacji!", "enable": "Włącz", + "enable_backup": "Włącz kopię zapasową", "enable_biometric_auth_description": "Wprowadź kod PIN aby włączyć logowanie biometryczne", "enabled": "Włączone", "end_date": "Do dnia", @@ -974,6 +988,7 @@ }, "exif": "Metadane EXIF", "exif_bottom_sheet_description": "Dodaj Opis...", + "exif_bottom_sheet_description_error": "Wystąpił błąd podczas aktualizacji opisu", "exif_bottom_sheet_details": "SZCZEGÓŁY", "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", @@ -994,6 +1009,8 @@ "explorer": "Eksplorator", "export": "Eksportuj", "export_as_json": "Eksportuj jako JSON", + "export_database": "Exportuj bazę danych", + "export_database_description": "Exportuj bazę danych SQLite", "extension": "Rozszerzenie", "external": "Zewnętrzny", "external_libraries": "Biblioteki Zewnętrzne", @@ -1045,6 +1062,9 @@ "haptic_feedback_switch": "Włącz technologię haptyczną", "haptic_feedback_title": "Technologia haptyczna", "has_quota": "Ma limit", + "hash_asset": "Hashuj zasób", + "hashed_assets": "Zahashowane zasoby", + "hashing": "Hashowanie", "header_settings_add_header_tip": "Dodaj nagłówek", "header_settings_field_validator_msg": "Wartość nie może być pusta", "header_settings_header_name_input": "Nazwa nagłówka", @@ -1077,6 +1097,7 @@ "host": "Host", "hour": "Godzina", "id": "ID", + "idle": "Bezczynny", "ignore_icloud_photos": "Ignoruj zdjęcia w iCloud", "ignore_icloud_photos_description": "Zdjęcia przechowywane w usłudze iCloud nie zostaną przesłane na serwer Immich", "image": "Zdjęcie", @@ -1134,6 +1155,7 @@ "language_no_results_title": "Nie znaleziono żadnych języków", "language_search_hint": "Szukaj języków...", "language_setting_description": "Wybierz swój preferowany język", + "large_files": "Duże pliki", "last_seen": "Ostatnio widziane", "latest_version": "Najnowsza Wersja", "latitude": "Szerokość geograficzna", @@ -1153,13 +1175,14 @@ "light": "Jasny", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", - "link_options": "Opcje linku", "link_to_oauth": "Połącz z OAuth", "linked_oauth_account": "Połączone konto OAuth", "list": "Lista", "loading": "Ładowanie", "loading_search_results_failed": "Ładowanie wyników wyszukiwania nie powiodło się", + "local": "Lokalny", "local_asset_cast_failed": "Nie można strumieniować zasobu, który nie został przesłany na serwer", + "local_assets": "Zasoby lokalne", "local_network": "Sieć lokalna", "local_network_sheet_info": "Aplikacja połączy się z serwerem za pośrednictwem tego adresu URL podczas korzystania z określonej sieci Wi-Fi", "location_permission": "Zezwolenie na lokalizację", @@ -1216,8 +1239,7 @@ "manage_your_devices": "Zarządzaj swoimi zalogowanymi urządzeniami", "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} zdjęcie", - "map_assets_in_bounds": "{count} zdjęć", + "map_assets_in_bounds": "{count, plural, one {# zdjęcie} few {# zdjęcia} other {# zdjęć}}", "map_cannot_get_user_location": "Nie można uzyskać lokalizacji użytkownika", "map_location_dialog_yes": "Tak", "map_location_picker_page_use_location": "Użyj tej lokalizacji", @@ -1316,6 +1338,7 @@ "no_results": "Brak wyników", "no_results_description": "Spróbuj użyć synonimu lub bardziej ogólnego słowa kluczowego", "no_shared_albums_message": "Stwórz album aby udostępnić zdjęcia i filmy osobom w Twojej sieci", + "no_uploads_in_progress": "Brak przesyłań w toku", "not_in_any_album": "Bez albumu", "not_selected": "Nie wybrano", "note_apply_storage_label_to_previously_uploaded assets": "Uwaga: Aby przypisać etykietę magazynowania do wcześniej przesłanych zasobów, uruchom", @@ -1353,6 +1376,7 @@ "original": "oryginalny", "other": "Inne", "other_devices": "Inne urządzenia", + "other_entities": "Inne byty", "other_variables": "Inne zmienne", "owned": "Posiadany", "owner": "Właściciel", @@ -1484,6 +1508,7 @@ "purchase_server_description_2": "Status wspierającego", "purchase_server_title": "Serwer", "purchase_settings_server_activated": "Klucz produktu serwera jest zarządzany przez administratora", + "queue_status": "Kolejkowanie {count}/{total}", "rating": "Ocena gwiazdkowa", "rating_clear": "Wyczyść ocenę", "rating_count": "{count, plural, one {# gwiazdka} other {# gwiazdek}}", @@ -1512,6 +1537,8 @@ "refreshing_faces": "Odświeżanie twarzy", "refreshing_metadata": "Odświeżanie metadanych", "regenerating_thumbnails": "Regenerowanie miniatur", + "remote": "Zdalny", + "remote_assets": "Zasoby zdalne", "remove": "Usuń", "remove_assets_album_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z albumu?", "remove_assets_shared_link_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z tego udostępnionego linku?", @@ -1549,19 +1576,25 @@ "reset_password": "Resetuj hasło", "reset_people_visibility": "Zresetuj widoczność osób", "reset_pin_code": "Zresetuj kod PIN", + "reset_sqlite": "Zresetuj bazę danych SQLite", + "reset_sqlite_confirmation": "Czy na pewno chcesz zresetować bazę danych SQLite? Wymagane będzie wylogowanie oraz ponowne zalogowanie, aby zsynchronizować dane", + "reset_sqlite_success": "Pomyślnie zresetowano bazę danych SQLite", "reset_to_default": "Przywróć ustawienia domyślne", "resolve_duplicates": "Rozwiąż problemy z duplikatami", "resolved_all_duplicates": "Rozwiązano wszystkie duplikaty", "restore": "Przywrócić", "restore_all": "Przywróć wszystko", + "restore_trash_action_prompt": "{count} przywrócono z kosza", "restore_user": "Przywróć użytkownika", "restored_asset": "Przywrócony zasób", "resume": "Wznów", "retry_upload": "Prześlij ponownie", "review_duplicates": "Przejrzyj duplikaty", + "review_large_files": "Przejrzyj duże pliki", "role": "Rola", "role_editor": "Edytor", "role_viewer": "Widz", + "running": "W trakcie", "save": "Zapisz", "save_to_gallery": "Zapisz w galerii", "saved_api_key": "Zapisany klucz API", @@ -1715,6 +1748,7 @@ "shared_link_clipboard_copied_massage": "Skopiowane do schowka", "shared_link_clipboard_text": "Link: {link}\nHasło: {password}", "shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia", + "shared_link_custom_url_description": "Otwórz udostępniony link z niestandardowym adresem URL", "shared_link_edit_description_hint": "Wprowadź opis udostępnienia", "shared_link_edit_expire_after_option_day": "1 dniu", "shared_link_edit_expire_after_option_days": "{count} dniach", @@ -1740,6 +1774,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Zarządzaj udostępnionymi linkami", "shared_link_options": "Opcje udostępniania linku", + "shared_link_password_description": "Wymagaj hasła dostępu dla udostępnionego linku", "shared_links": "Udostępnione linki", "shared_links_description": "Udostępnij zdjęcia oraz filmy przez link", "shared_photos_and_videos_count": "{assetCount, plural, one {# udostępnione zdjęcie lub film.} other {# udostępnione zdjęcia i filmy.}}", @@ -1815,6 +1850,7 @@ "storage_quota": "Limit pamięci", "storage_usage": "{used} z {available} użyte", "submit": "Zatwierdź", + "success": "Sukces", "suggestions": "Sugestie", "sunrise_on_the_beach": "Wschód słońca na plaży", "support": "Wsparcie", @@ -1824,6 +1860,8 @@ "sync": "Synchronizuj", "sync_albums": "Synchronizuj albumy", "sync_albums_manual_subtitle": "Zsynchronizuj wszystkie przesłane filmy i zdjęcia z wybranymi albumami kopii zapasowych", + "sync_local": "Synchronizacja lokalna", + "sync_remote": "Synchronizacja zdalna", "sync_upload_album_setting_subtitle": "Twórz i przesyłaj swoje zdjęcia i filmy do wybranych albumów w Immich", "tag": "Etykieta", "tag_assets": "Ustaw etykiety zasobów", @@ -1834,6 +1872,7 @@ "tag_updated": "Uaktualniono etykietę: {tag}", "tagged_assets": "Przypisano etykietę {count, plural, one {# zasobowi} other {# zasobom}}", "tags": "Etykiety", + "tap_to_run_job": "Uruchom zadanie", "template": "Szablon", "theme": "Motyw", "theme_selection": "Wybór motywu", @@ -1913,10 +1952,13 @@ "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", "upload": "Prześlij", + "upload_action_prompt": "{count} w kolejce do wysłania", "upload_concurrency": "Współbieżność wysyłania", + "upload_details": "Szczegóły przesyłania", "upload_dialog_info": "Czy chcesz wykonać kopię zapasową wybranych zasobów na serwerze?", "upload_dialog_title": "Prześlij Zasób", "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błędem} other {# błędami}}. Odśwież stronę, aby zobaczyć nowo przesłane zasoby.", + "upload_finished": "Przesyłanie zakończone", "upload_progress": "Pozostałe {remaining, number} - Przetworzone {processed, number}/{total, number}", "upload_skipped_duplicates": "Pominięto {count, plural, one {# zduplikowany zasób} few {# zduplikowane zasoby} other {# zduplikowanych zasobów}}", "upload_status_duplicates": "Duplikaty", @@ -1925,6 +1967,7 @@ "upload_success": "Przesyłanie powiodło się, odśwież stronę, aby zobaczyć nowo przesłane zasoby.", "upload_to_immich": "Prześlij do Immich ({count})", "uploading": "Przesyłanie", + "uploading_media": "Przesyłanie multimediów", "url": "URL", "usage": "Użycie", "use_biometric": "Użyj biometrii", @@ -1964,6 +2007,7 @@ "view_album": "Wyświetl Album", "view_all": "Pokaż wszystkie", "view_all_users": "Pokaż wszystkich użytkowników", + "view_details": "Zobacz szczegóły", "view_in_timeline": "Pokaż na osi czasu", "view_link": "Zobacz link", "view_links": "Pokaż łącza", diff --git a/i18n/pt.json b/i18n/pt.json index 1878c9b9cc..efe4060730 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -397,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem a certeza de que quer eliminar o álbum {album}?", "album_delete_confirmation_description": "Se este álbum for partilhado, os outros utilizadores deixam de o poder aceder.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -406,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover utilizador?", "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", + "album_search_not_found": "Nenhum álbum encontrado segundo a pesquisa", "album_share_no_users": "Parece que tem este álbum partilhado com todos os utilizadores ou que não existem utilizadores com quem o partilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receber uma notificação por e-mail quando um álbum partilhado tiver novos ficheiros", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão de organização do álbum", "albums_default_sort_order_description": "Ordem inicial dos ficheiros ao criar novos álbuns.", "albums_feature_description": "Coleções de ficheiros que podem ser partilhados com outros utilizadores.", + "albums_on_device_count": "Álbums no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -508,6 +511,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Cópia de segurança", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", "backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Estado de Sincronização Beta", + "beta_sync_subtitle": "Gerir o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação biométrica ativada", "biometric_locked_out": "Está impedido de utilizar a autenticação biométrica", "biometric_no_options": "Sem opções biométricas disponíveis", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Limpar cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista negra da aplicação", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista de bloqueio da aplicação", "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -605,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "A cancelar", "cannot_merge_people": "Não foi possível unir pessoas", "cannot_undo_this_action": "Não é possível anular esta ação!", "cannot_update_the_description": "Não foi possível atualizar a descrição", @@ -737,24 +744,27 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Eliminar", - "delete_action_prompt": "{count} eliminados permanentemente", - "delete_album": "Eliminar álbum", - "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", + "delete_action_confirmation_message": "Tem a certeza de que quer eliminar este ficheiro? Está ação irá mover o ficheiro para a reciclagem do servidor e perguntar se quer apagá-lo localmente", + "delete_action_prompt": "{count} eliminados", + "delete_album": "Apagar álbum", + "delete_api_key_prompt": "Tem a certeza de que deseja remover esta chave de API?", "delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo", "delete_dialog_alert_local": "Estes arquivos serão permanentemente excluídos do seu dispositivo, mas continuarão disponíveis no servidor Immich", "delete_dialog_alert_local_non_backed_up": "Não há backup de alguns dos arquivos no servidor e eles serão excluídos permanentemente do seu dispositivo", "delete_dialog_alert_remote": "Estes arquivos serão permanentemente excluídos do servidor Immich", - "delete_dialog_ok_force": "Excluir mesmo assim", + "delete_dialog_ok_force": "Confirmo que quero excluir", "delete_dialog_title": "Excluir Permanentemente", "delete_duplicates_confirmation": "Tem a certeza de que deseja eliminar permanentemente estes itens duplicados?", "delete_face": "Remover rosto", - "delete_key": "Eliminar chave", + "delete_key": "Apagar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar link de partilha", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Eliminar etiqueta", @@ -765,6 +775,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o registo para obter mais detalhes", + "deselect_all": "Remover seleção de tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -839,6 +850,7 @@ "empty_trash": "Esvaziar reciclagem", "empty_trash_confirmation": "Tem a certeza de que deseja esvaziar a reciclagem? Isto removerá todos os ficheiros da reciclagem do Immich permanentemente.\nNão é possível anular esta ação!", "enable": "Ativar", + "enable_backup": "Ativar Cópia de Segurança", "enable_biometric_auth_description": "Insira o código PIN para ativar a autenticação biométrica", "enabled": "Ativado", "end_date": "Data final", @@ -995,6 +1007,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Base de Dados", + "export_database_description": "Exportar a Base de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -1046,6 +1060,9 @@ "haptic_feedback_switch": "Habilitar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Tem quota", + "hash_asset": "Criptografar ficheiro", + "hashed_assets": "Ficheiros criptografados", + "hashing": "A criptografar", "header_settings_add_header_tip": "Adicionar cabeçalho", "header_settings_field_validator_msg": "Campo deve ser preenchido", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1078,6 +1095,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Em espera", "ignore_icloud_photos": "ignorar fotos no iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão carregadas para o servidor do Immich", "image": "Imagem", @@ -1154,13 +1172,14 @@ "light": "Claro", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Associada", "list": "Lista", "loading": "A Carregar", "loading_search_results_failed": "Ocorreu um erro ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um ficheiro que não tenha sido enviado antes para o servidor", + "local_assets": "Ficheiros Locais", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1217,8 +1236,7 @@ "manage_your_devices": "Gerir os seus dispositivos com sessão iniciada", "manage_your_oauth_connection": "Gerir a sua ligação ao OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Impossível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localização", @@ -1317,6 +1335,7 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinónimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um álbum para partilhar fotos e vídeos com pessoas na sua rede", + "no_uploads_in_progress": "Nenhum carregamento em curso", "not_in_any_album": "Não está em nenhum álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -1354,6 +1373,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1485,6 +1505,7 @@ "purchase_server_description_2": "Status de apoiante", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto do servidor é gerida pelo administrador", + "queue_status": "Em fila {count}/{total}", "rating": "Classificação por estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1513,6 +1534,8 @@ "refreshing_faces": "A atualizar rostos", "refreshing_metadata": "A atualizar metadados", "regenerating_thumbnails": "A atualizar miniaturas", + "remote": "Remoto", + "remote_assets": "Ficheiros Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem a certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} deste link partilhado?", @@ -1550,11 +1573,15 @@ "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", "reset_pin_code": "Repor código PIN", + "reset_sqlite": "Reiniciar Base de Dados SQLite", + "reset_sqlite_confirmation": "Tem a certeza de que quer reiniciar a base de dados SQLite? Vai ter de terminar a sessão e entrar outra vez para sincronizar os dados de novo", + "reset_sqlite_success": "Base de dados SQLite reiniciada com sucesso", "reset_to_default": "Repor predefinições", "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todos os itens duplicados resolvidos", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da reciclagem", "restore_user": "Restaurar utilizador", "restored_asset": "Ficheiro restaurado", "resume": "Continuar", @@ -1563,6 +1590,7 @@ "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "A executar", "save": "Guardar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API guardada", @@ -1816,6 +1844,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Apoio", @@ -1825,6 +1854,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronizar todas as fotos e vídeos enviados para o álbum de backup selecionado", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar ficheiros", @@ -1835,6 +1866,7 @@ "tag_updated": "Atualizada a etiqueta: {tag}", "tagged_assets": "Etiquetado {count, plural, one {# ficheiros} other {# ficheiros}}", "tags": "Etiquetas", + "tap_to_run_job": "Tocar para executar tarefa", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1914,10 +1946,13 @@ "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", + "upload_action_prompt": "{count} à espera de carregar", "upload_concurrency": "Carregamentos em simultâneo", + "upload_details": "Detalhes do Carregamento", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos ficheiros enviados.", + "upload_finished": "Carregamento acabado", "upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# Ignorado ficheiro duplicado} other {# Ignorados ficheiros duplicados}}", "upload_status_duplicates": "Duplicados", @@ -1926,6 +1961,7 @@ "upload_success": "Carregamento realizado com sucesso, atualize a página para ver os novos ficheiros carregados.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "A carregar media", "url": "URL", "usage": "Utilização", "use_biometric": "Utilizar dados biométricos", @@ -1965,6 +2001,7 @@ "view_album": "Ver Álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os utilizadores", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 1af3bda2ac..c8bf543731 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Gerenciar configurações de metadados", "migration_job": "Migração", "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Fazer o reconhecimento facial dos novos rostos detectados", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novos rostos", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza do banco de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpe dados velhos e expirados do banco de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir dos arquivos", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Adiciona na fila de geração de miniaturas as fotos ainda sem miniaturas", + "nightly_tasks_settings": "Configurações de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerenciar tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora que o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento dos usuários, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", @@ -359,6 +373,8 @@ "admin_password": "Senha do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Teste a nova interface do aplicativo", + "advanced_settings_beta_timeline_title": "Linha do tempo Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Use esta opção para filtrar mídias durante a sincronização com base em critérios alternativos. Tente esta opção somente se o aplicativo estiver com problemas para detectar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar filtro alternativo de sincronização de álbum de dispositivo", "advanced_settings_log_level_title": "Nível de log: {level}", @@ -381,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem certeza de que deseja excluir o álbum {album}?", "album_delete_confirmation_description": "Se este álbum é compartilhado, os outros usuários não conseguiram mais acessá-lo.", + "album_deleted": "Álbum deletado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -390,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover usuário?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", + "album_search_not_found": "Não há álbum que corresponda à sua pesquisa", "album_share_no_users": "Parece que você já compartilhou este álbum com todos os usuários ou não há nenhum usuário para compartilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificação por e-mail quando um álbum compartilhado tiver novos recursos", @@ -409,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão do álbum", "albums_default_sort_order_description": "Ordem padrão dos arquivos ao criar novos álbuns.", "albums_feature_description": "Coleções de arquivos que podem ser compartilhados com outros usuários.", + "albums_on_device_count": "Álbuns no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -492,6 +511,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -555,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Status da sincronização Beta", + "beta_sync_subtitle": "Configurar o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação por biometria ativada", "biometric_locked_out": "Sua autenticação por biometria está bloqueada", "biometric_no_options": "Não há opções de biometria disponíveis", @@ -589,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "Não é possível mesclar pessoas", "cannot_undo_this_action": "Você não pode desfazer esta ação!", "cannot_update_the_description": "Não é possível atualizar a descrição", @@ -642,7 +665,7 @@ "comments_are_disabled": "Comentários estão desativados", "common_create_new_album": "Criar novo álbum", "common_server_error": "Verifique a sua conexão de rede, certifique-se de que o servidor está acessível e de que as versões do aplicativo e servidor são compatíveis.", - "completed": "Sucesso", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", "confirm_delete_face": "Tem certeza que deseja remover a rosto de {name} deste arquivo?", @@ -702,6 +725,7 @@ "current_server_address": "Endereço atual do servidor", "custom_locale": "Localização Customizada", "custom_locale_description": "Formatar datas e números baseados na linguagem e região", + "custom_url": "URL personalizada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", @@ -721,7 +745,8 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", - "delete_action_prompt": "{count} deletados permanentemente", + "delete_action_confirmation_message": "Confirma deletar este arquivo? O arquivo será enviado para a lixeira do servidor e depois perguntará se deseja deletar do seu dispositivo local", + "delete_action_prompt": "{count} deletados", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serão excluídos permanentemente do Immich e do seu dispositivo", @@ -735,9 +760,12 @@ "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", + "delete_local_action_prompt": "{count} deletados do dispositivo local", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup feito", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir restante", + "delete_permanently": "Deletar permanentemente", + "delete_permanently_action_prompt": "{count} Deletado permanentemente", "delete_shared_link": "Excluir link de compartilhamento", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Excluir marcador", @@ -748,6 +776,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o log para mais detalhes", + "deselect_all": "Desselecionar tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -765,6 +794,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Baixar", + "download_action_prompt": "Baixando {count} arquivos", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -821,6 +851,7 @@ "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá permanentemente do Immich todos os arquivos que estão na lixeira.\nVocê não pode desfazer esta ação!", "enable": "Habilitar", + "enable_backup": "Ativar Backup", "enable_biometric_auth_description": "Insira seu código PIN para ativar a autenticação por biometria", "enabled": "Habilitado", "end_date": "Data final", @@ -977,6 +1008,8 @@ "explorer": "Explorar", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Banco de Dados", + "export_database_description": "Exportar o Banco de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -1028,6 +1061,9 @@ "haptic_feedback_switch": "Ativar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Cota", + "hash_asset": "Calcular hash dos arquivos", + "hashed_assets": "Com hash", + "hashing": "Calculando", "header_settings_add_header_tip": "Adicionar Cabeçalho", "header_settings_field_validator_msg": "O valor não pode estar vazio", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1060,6 +1096,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Inativo", "ignore_icloud_photos": "Ignorar fotos do iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão enviadas para o servidor do Immich", "image": "Imagem", @@ -1132,16 +1169,18 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Curtida excluída", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Vinculada", "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um arquivo que não foi enviado ao servidor", + "local_assets": "Arquivos no dispositivo", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1172,7 +1211,7 @@ "login_form_err_trailing_whitespace": "Há um espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Erro de login com OAuth, verifique a URL do servidor", "login_form_failed_get_oauth_server_disable": "O recurso OAuth não está disponível neste servidor", - "login_form_failed_login": "Erro ao fazer login, verifique a url do servidor, e-mail e senha", + "login_form_failed_login": "Erro ao fazer login, verifique a URL do servidor, e-mail e senha", "login_form_handshake_exception": "Houve um erro de autorização com o servidor. Se estiver utilizando um certificado auto assinado, ative o suporte a isso nas configurações.", "login_form_password_hint": "senha", "login_form_save_login": "Permaneçer conectado", @@ -1198,8 +1237,7 @@ "manage_your_devices": "Gerenciar seus dispositivos logados", "manage_your_oauth_connection": "Gerenciar sua conexão OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Não foi possível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Use esta localização", @@ -1298,6 +1336,7 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinônimo ou uma palavra-chave mais geral", "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", + "no_uploads_in_progress": "Nenhum envio em progresso", "not_in_any_album": "Fora de álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", @@ -1335,6 +1374,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1466,6 +1506,7 @@ "purchase_server_description_2": "Status de Contribuidor", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave do produto para servidor é gerenciada pelo administrador", + "queue_status": "Na fila {count} de {total}", "rating": "Estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1494,6 +1535,8 @@ "refreshing_faces": "Atualizando rostos", "refreshing_metadata": "Atualizando metadados", "regenerating_thumbnails": "Regenerando miniaturas", + "remote": "Remoto", + "remote_assets": "Arquivos Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", @@ -1531,11 +1574,15 @@ "reset_password": "Resetar senha", "reset_people_visibility": "Resetar pessoas ocultas", "reset_pin_code": "Redefinir código PIN", + "reset_sqlite": "Redefinir o Banco de Dados SQLite", + "reset_sqlite_confirmation": "Realmente deseja redefinir o banco de dados SQLite? Será necessário sair e entrar em sua conta novamente para ressincronizar os dados", + "reset_sqlite_success": "Banco de dados SQLite redefinido com sucesso", "reset_to_default": "Redefinir para a configuração padrão", "resolve_duplicates": "Resolver duplicatas", "resolved_all_duplicates": "Todas duplicidades resolvidas", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da lixeira", "restore_user": "Restaurar usuário", "restored_asset": "Arquivo restaurado", "resume": "Continuar", @@ -1544,6 +1591,7 @@ "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "Executando", "save": "Salvar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API salva", @@ -1675,10 +1723,11 @@ "settings_saved": "Configurações salvas", "setup_pin_code": "Criar um código PIN", "share": "Compartilhar", + "share_action_prompt": "{count} arquivos compartilhados", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", - "share_link": "Compartilhar Link", + "share_link": "Criar Link", "shared": "Compartilhado", "shared_album_activities_input_disable": "Comentários desativados", "shared_album_activity_remove_content": "Deseja excluir esta atividade?", @@ -1696,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Copiado para a área de transferência", "shared_link_clipboard_text": "Link: {link}\nSenha: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_custom_url_description": "Acessar este link com uma URL personalizada", "shared_link_edit_description_hint": "Digite a descrição do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dias", @@ -1721,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link compartilhado", + "shared_link_password_description": "Exija uma senha para acessar este link compartilhado", "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", @@ -1776,6 +1827,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Agrupar", + "stack_action_prompt": "{count} agrupados", "stack_duplicates": "Agrupar duplicados", "stack_select_one_photo": "Selecione uma foto principal para o grupo", "stack_selected_photos": "Agrupar fotos selecionadas", @@ -1795,6 +1847,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Ajuda", @@ -1804,6 +1857,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronize todos as fotos e vídeos enviados para os álbuns de backup selecionados", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Marcador", "tag_assets": "Marcar arquivos", @@ -1814,6 +1869,7 @@ "tag_updated": "Marcador foi atualizado: {tag}", "tagged_assets": "{count, plural, one {# Arquivo marcado} other {# Arquivos marcados}}", "tags": "Marcadores", + "tap_to_run_job": "Toque para executar", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1886,16 +1942,20 @@ "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unselect_all_in": "Remover seleção de {group}", "unstack": "Retirar do grupo", + "unstack_action_prompt": "{count} desagrupados", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", "upload": "Enviar", + "upload_action_prompt": "{count} na fila de envio", "upload_concurrency": "Envios simultâneos", + "upload_details": "Detalhes do envio", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos.", + "upload_finished": "Envio finalizado", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", @@ -1904,6 +1964,7 @@ "upload_success": "Enviado com sucesso, atualize a página para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "Enviando mídia", "url": "URL", "usage": "Uso", "use_biometric": "Usar biometria", @@ -1924,6 +1985,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização da conta", "username": "Nome do usuário", "users": "Usuários", + "users_added_to_album_count": "{count, plural, one {# usuário adicionado} other {# usuários adicionados}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1942,6 +2004,7 @@ "view_album": "Ver álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os usuários", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", diff --git a/i18n/ro.json b/i18n/ro.json index 46197e943f..40c50a317d 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -166,6 +166,19 @@ "metadata_settings_description": "Gestionează setările pentru metadate", "migration_job": "Migrare", "migration_job_description": "Migrați miniaturile pentru elemente și fețe la cea mai recentă structură de foldere", + "nightly_tasks_cluster_faces_setting_description": "Rulează recunoașterea facială pe fețele noi recunoscute", + "nightly_tasks_cluster_new_faces_setting": "Grupează fetele noi", + "nightly_tasks_database_cleanup_setting": "Sarcini curățare baze de date", + "nightly_tasks_database_cleanup_setting_description": "Curată date vechi, expirate din baza de date", + "nightly_tasks_generate_memories_setting": "Generare memorii", + "nightly_tasks_generate_memories_setting_description": "Creează amintiri noi din resurse", + "nightly_tasks_missing_thumbnails_setting": "Generează miniaturi lipsă", + "nightly_tasks_settings": "Setări pentru sarcinile nocturne", + "nightly_tasks_settings_description": "Gestionați sarcinile nocturne", + "nightly_tasks_start_time_setting": "Ora de începere", + "nightly_tasks_start_time_setting_description": "Ora la care serverul începe să execute sarcinile nocturne", + "nightly_tasks_sync_quota_usage_setting": "Utilizarea cotei de sincronizare", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizați cota de stocare a utilizatorului, în funcție de utilizarea actuală", "no_paths_added": "Nicio cale adăugată", "no_pattern_added": "Niciun tipar adăugat", "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele încărcate anterior, executați", @@ -196,6 +209,8 @@ "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override_description": "Activați atunci când furnizorul OAuth nu permite un URI mobil, precum ''{callback}''", + "oauth_role_claim": "Revendicare de rol", + "oauth_role_claim_description": "Acordă automat acces de administrator în funcție de prezența acestei revendicări. Revendicarea poate avea fie 'utilizator', fie 'administrator'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestionați setările de conectare OAuth", "oauth_settings_more_details": "Pentru mai multe detalii despre aceastǎ funcționalitate, verificǎ documentația.", @@ -357,10 +372,12 @@ "admin_password": "Parolă Administrator", "administration": "Administrare", "advanced": "Avansat", + "advanced_settings_beta_timeline_subtitle": "Încearcă noua experiență în aplicație", + "advanced_settings_beta_timeline_title": "Cronologie beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media în timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă întâmpinați probleme cu aplicația la detectarea tuturor albumelor.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizați filtrul alternativ de sincronizare a albumelor de pe dispozitiv", "advanced_settings_log_level_title": "Nivel log: {level}", - "advanced_settings_prefer_remote_subtitle": "Unele dispozitive întâmpină dificultăți în încărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a încărca imaginile de la distanță în schimb.", + "advanced_settings_prefer_remote_subtitle": "Unele dispozitive încarcă extrem de lent miniaturile din resursele locale. Activați această setare pentru a încărca imagini la distanță.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", "advanced_settings_proxy_headers_title": "Antete Proxy", @@ -379,6 +396,7 @@ "album_cover_updated": "Coperta albumului a fost actualizată", "album_delete_confirmation": "Ești sigur că vrei să ștergi albumul {album}?", "album_delete_confirmation_description": "Dacă acest album este partajat, alți utilizatori nu vor mai putea accesa.", + "album_deleted": "Album șters", "album_info_card_backup_album_excluded": "EXCLUSE", "album_info_card_backup_album_included": "INCLUSE", "album_info_updated": "Informații album actualizate", @@ -388,6 +406,7 @@ "album_options": "Opțiuni album", "album_remove_user": "Eliminare utilizator?", "album_remove_user_confirmation": "Ești sigur că dorești eliminarea {user}?", + "album_search_not_found": "Nu s-au găsit albume care să corespundă căutării dumneavoastră", "album_share_no_users": "Se pare că ai partajat acest album cu toți utilizatorii sau nu ai niciun utilizator cu care să-l partajezi.", "album_updated": "Album actualizat", "album_updated_setting_description": "Primiți o notificare prin e-mail când un album partajat are elemente noi", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", + "albums_on_device_count": "{count} albume pe dispozitiv", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -490,6 +510,7 @@ "back_close_deselect": "Înapoi, închidere sau deselectare", "background_location_permission": "Permisiune locație în fundal", "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi împrăștiate în mai multe albume. Prin urmare, albumele pot fi incluse sau excluse în timpul procesului de backup.", @@ -553,6 +574,8 @@ "backup_options_page_title": "Opțiuni Backup", "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backward": "În sens invers", + "beta_sync": "Starea sincronizării Beta", + "beta_sync_subtitle": "Gestionați noul sistem de sincronizare", "biometric_auth_enabled": "Autentificare biometrică activată", "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", @@ -570,7 +593,7 @@ "cache_settings_clear_cache_button": "Șterge cache", "cache_settings_clear_cache_button_title": "Șterge memoria cache a aplicatiei. Performanța aplicației va fi semnificativ afectată până când va fi reconstruită.", "cache_settings_duplicated_assets_clear_button": "ȘTERGE", - "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri care sunt pe lista neagră a aplicației", + "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri ignorate în lista aplicației", "cache_settings_duplicated_assets_title": "Resurse duplicate ({count})", "cache_settings_statistics_album": "Miniaturi pentru librării", "cache_settings_statistics_full": "Fotografii complete", @@ -587,6 +610,7 @@ "cancel": "Anulați", "cancel_search": "Anulați căutarea", "canceled": "Anulat", + "canceling": "Se anulează", "cannot_merge_people": "Nu se pot îmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", @@ -700,11 +724,14 @@ "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele în funcție de limbă și regiune", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Întunecat", "dark_theme": "Comută tema întunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", + "date_format": "E, LLL d, y • h:mm a", "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", @@ -716,7 +743,8 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele în funcție de regiunea browserului dvs", "delete": "Ștergere", - "delete_action_prompt": "{count} șterse permanent", + "delete_action_confirmation_message": "Sigur vrei să ștergi acest element? Această acțiune va muta elementul în coșul de gunoi al serverului și te va întreba dacă vrei să-l ștergi local", + "delete_action_prompt": "{count} șterse", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -730,9 +758,12 @@ "delete_key": "Ștergere cheie", "delete_library": "Ștergere biblioteca", "delete_link": "Ștergere link", + "delete_local_action_prompt": "{count} șterse local", "delete_local_dialog_ok_backed_up_only": "Șterge doar fișierele pentru care s-a făcut backup", "delete_local_dialog_ok_force": "Șterge oricum", "delete_others": "Ștergeți celelalte", + "delete_permanently": "Șterge permanent", + "delete_permanently_action_prompt": "{count} șterse permanent", "delete_shared_link": "Ștergere link partajat", "delete_shared_link_dialog_title": "Șterge link distribuire", "delete_tag": "Ștergere etichetă", @@ -743,6 +774,7 @@ "description": "Descriere", "description_input_hint_text": "Adaugă descriere...", "description_input_submit_error": "Eroare actualizare descriere, verifică log-urile pentru mai multe detalii", + "deselect_all": "Deselectează toate", "details": "Detalii", "direction": "Direcție", "disabled": "Dezactivat", @@ -760,6 +792,7 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_action_prompt": "Se descarcă {count} elemente", "download_canceled": "Descărcare anulată", "download_complete": "Descărcare completă", "download_enqueue": "Descărcare în coadă", @@ -768,10 +801,17 @@ "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri încorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile încorporate în fotografiile în mișcare ca fișier separat", + "download_notfound": "Descărcare negăsită", + "download_paused": "Descărcarea a fost întreruptă", "download_settings": "Descărcați", "download_settings_description": "Gestionați setările legate de descărcarea resurselor", + "download_started": "Descărcarea a început", + "download_sucess": "Descărcare reușită", + "download_sucess_android": "Fișierul media a fost descărcat în DCIM/Immich", + "download_waiting_to_retry": "Se așteaptă o nouă încercare", "downloading": "Se descarcă", "downloading_asset_filename": "Se descarcă resursa {filename}", + "downloading_media": "Se descarcă fișierele media", "drop_files_to_upload": "Trageți fișierele aici pentru a le încărca", "duplicates": "Duplicate", "duplicates_description": "Rezolvați fiecare grup indicând care sunt duplicate, dacă există", @@ -781,6 +821,8 @@ "edit_avatar": "Editare avatar", "edit_date": "Editare dată", "edit_date_and_time": "Editare dată și oră", + "edit_description": "Editează descrierea", + "edit_description_prompt": "Vă rugăm să selectați o descriere nouă:", "edit_exclusion_pattern": "Editarea modelului de excludere", "edit_faces": "Editare fețe", "edit_import_path": "Editare cale de import", @@ -788,6 +830,7 @@ "edit_key": "Tastă de editare", "edit_link": "Editare link", "edit_location": "Editare locație", + "edit_location_action_prompt": "{count} locație(i) editată(e)", "edit_location_dialog_title": "Locație", "edit_name": "Editare nume", "edit_people": "Editare persoane", @@ -795,19 +838,31 @@ "edit_title": "Editare Titlu", "edit_user": "Editare utilizator", "edited": "Editat", + "editor": "Editor", "editor_close_without_save_prompt": "Schimbările nu vor fi salvate", "editor_close_without_save_title": "Închideți editorul?", "editor_crop_tool_h2_aspect_ratios": "Raporturi de aspect", "editor_crop_tool_h2_rotation": "Rotire", + "email": "Adresă de mail", + "email_notifications": "Notificări e-mail", + "empty_folder": "Acest dosar este gol", "empty_trash": "Goliți coșul de gunoi", "empty_trash_confirmation": "Sunteți sigur că doriți să goliți coșul de gunoi? Acest lucru va elimina definitiv din Immich toate resursele din coșul de gunoi.\nNu puteți anula această acțiune!", "enable": "Permite", + "enable_backup": "Activează backup", + "enable_biometric_auth_description": "Introduceți codul PIN pentru a activa autentificarea biometrică", "enabled": "Activat", "end_date": "Data de încheiere", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Pus în coadă", + "enter_wifi_name": "Introduceți numele rețelei Wi-Fi", + "enter_your_pin_code": "Introduceți codul PIN", + "enter_your_pin_code_subtitle": "Introduceți codul PIN pentru a accesa folderul blocat", "error": "Eroare", + "error_change_sort_album": "Nu s-a putut modifica ordinea de sortare a albumului", "error_delete_face": "Eroare la ștergerea feței din activ", "error_loading_image": "Eroare la încărcarea imaginii", + "error_saving_image": "Eroare: {error}", + "error_tag_face_bounding_box": "Eroare la etichetarea feței - nu se pot obține coordonatele casetei de delimitare", "error_title": "Eroare - ceva nu a mers", "errors": { "cannot_navigate_next_asset": "Nu se poate naviga către următoarea resursă", @@ -835,10 +890,12 @@ "failed_to_keep_this_delete_others": "Nu s-a putut păstra acest material respectiv nu s-au putut șterge celelalte materiale", "failed_to_load_asset": "Eșec la încărcarea resursei", "failed_to_load_assets": "Eșec la încărcarea resurselor", + "failed_to_load_notifications": "Nu s-au putut încărca notificările", "failed_to_load_people": "Eșec la încărcarea persoanelor", "failed_to_remove_product_key": "Eșec la eliminarea cheii de produs", "failed_to_stack_assets": "Eșec la combinarea resurselor", "failed_to_unstack_assets": "Eșec la desfășurarea resurselor", + "failed_to_update_notification_status": "Nu s-a putut actualiza starea notificării", "import_path_already_exists": "Această cale de import există deja.", "incorrect_email_or_password": "E-mail sau parolă incorect/ă", "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", @@ -855,6 +912,7 @@ "unable_to_archive_unarchive": "Nu se poate {archived, select, true {arhiva} other {dezarhiva}}", "unable_to_change_album_user_role": "Nu se poate schimba rolul utilizatorului de album", "unable_to_change_date": "Imposibil de schimbat data", + "unable_to_change_description": "Nu se poate schimba descrierea", "unable_to_change_favorite": "Nu se pot modifica favoritele pentru resursa", "unable_to_change_location": "Imposibil de schimbat locația", "unable_to_change_password": "Imposibil de schimbat parola", @@ -898,6 +956,7 @@ "unable_to_remove_partner": "Imposibil de eliminat partenerul", "unable_to_remove_reaction": "Nu se poate elimina reacția", "unable_to_reset_password": "Imposibil de resetat parola", + "unable_to_reset_pin_code": "Nu se poate reseta codul PIN", "unable_to_resolve_duplicate": "Nu se poate rezolva duplicatul", "unable_to_restore_assets": "Nu se pot restaura resursele", "unable_to_restore_trash": "Nu se poate restaura coșul de gunoi", @@ -930,11 +989,16 @@ "exif_bottom_sheet_details": "DETALII", "exif_bottom_sheet_location": "LOCAȚIE", "exif_bottom_sheet_people": "PERSOANE", + "exif_bottom_sheet_person_add_person": "Adăugați nume", + "exif_bottom_sheet_person_age_months": "Vârstă {months} luni", + "exif_bottom_sheet_person_age_year_months": "Vârstă 1 an, {months} luni", + "exif_bottom_sheet_person_age_years": "Vârstă {years}", "exit_slideshow": "Ieșire din Prezentare", "expand_all": "Extindeți-le pe toate", "experimental_settings_new_asset_list_subtitle": "Acțiune în desfășurare", "experimental_settings_new_asset_list_title": "Activează grila experimentală de fotografii", "experimental_settings_subtitle": "Folosește pe propria răspundere!", + "experimental_settings_title": "Experimental", "expire_after": "Expiră după", "expired": "Expirat", "expires_date": "Expiră la {date}", @@ -942,13 +1006,20 @@ "explorer": "Explorator", "export": "Exportare", "export_as_json": "Exportare ca JSON", + "export_database": "Exportați baza de date", + "export_database_description": "Exportați baza de date SQLite", "extension": "Extensie", "external": "Extern", "external_libraries": "Biblioteci Externe", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Rețea externă", + "external_network_sheet_info": "Când nu se află în rețeaua Wi-Fi preferată, aplicația se va conecta la server prin prima dintre adresele URL de mai jos pe care o poate accesa, începând de sus în jos", "face_unassigned": "Nealocat", + "failed": "Eșuat", + "failed_to_authenticate": "Autentificarea nu a reușit", "failed_to_load_assets": "Nu s-au încărcat activele", + "failed_to_load_folder": "Nu s-a putut încărca folderul", "favorite": "Favorit", + "favorite_action_prompt": "{count} adăugate la Favorite", "favorite_or_unfavorite_photo": "Fotografie preferată sau nepreferată", "favorites": "Favorite", "favorites_page_no_favorites": "Nu au fost găsite resurse favorite", @@ -959,25 +1030,41 @@ "file_name_or_extension": "Numele sau extensia fișierului", "filename": "Numele fișierului", "filetype": "Tipul fișierului", + "filter": "Filtre", "filter_people": "Filtrați persoanele", "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", "fix_incorrect_match": "Remediați potrivirea incorectă", + "folder": "Dosar", + "folder_not_found": "Dosar negăsit", "folders": "Foldere", "folders_feature_description": "Răsfoire în conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", "forward": "Redirecționare", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Această funcție încarcă resurse externe de la Google pentru a funcționa.", + "general": "General", "get_help": "Obțineți Ajutor", + "get_wifiname_error": "Nu s-a putut obține numele rețelei Wi-Fi. Asigurați-vă că ați acordat permisiunile necesare și că sunteți conectat la o rețea Wi-Fi", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", + "grant_permission": "Acordați permisiunea", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", "group_no": "Fără grupare", "group_owner": "Grupați după proprietar", "group_places_by": "Grupare locuri după...", "group_year": "Grupați după an", + "haptic_feedback_switch": "Activează feedback-ul haptic", + "haptic_feedback_title": "Feedback haptic", "has_quota": "Are spațiu de stocare", + "header_settings_add_header_tip": "Adăugați antet", + "header_settings_field_validator_msg": "Valoarea nu poate fi goală", + "header_settings_header_name_input": "Numele antetului", + "header_settings_header_value_input": "Valoarea antetului", + "headers_settings_tile_subtitle": "Definiți header-urile proxy pe care aplicația ar trebui să le trimită cu fiecare solicitare de rețea", + "headers_settings_tile_title": "Header-uri proxy personalizate", "hi_user": "Bună {name} ({email})", "hide_all_people": "Ascundeți toate persoanele", "hide_gallery": "Ascundeți galeria", @@ -997,10 +1084,16 @@ "home_page_favorite_err_local": "Resursele locale nu pot fi adăugate la favorite încă, omitere", "home_page_favorite_err_partner": "Momentan nu se pot adăuga fișierele partenerului la favorite, omitere", "home_page_first_time_notice": "Dacă este prima dată când utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel încât cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume", + "home_page_locked_error_local": "Nu se pot muta resursele locale în folderul blocat, se omit", + "home_page_locked_error_partner": "Nu se pot muta materialele partenerului în folderul blocat, se omit.", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot încărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", + "id": "ID", + "idle": "Inactiv", + "ignore_icloud_photos": "Ignoră fotografiile din iCloud", + "ignore_icloud_photos_description": "Fotografiile stocate pe iCloud nu vor fi încărcate pe serverul Immich", "image": "Imagine", "image_alt_text_date": "{isVideo, select, true {Video} other {imagine}} preluată în {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {imagine}} preluată cu {person1} în {date}", @@ -1012,6 +1105,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1} și {person2} în {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {person3} în {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {additionalCount, number} alții în {date}", + "image_saved_successfully": "Imaginea a fost salvată", + "image_viewer_page_state_provider_download_started": "Descărcare începută", "image_viewer_page_state_provider_download_success": "Descărcare cu succes", "image_viewer_page_state_provider_share_error": "Eroare distribuire", "immich_logo": "Logo Immich", @@ -1032,8 +1127,15 @@ "night_at_midnight": "În fiecare noapte la miezul nopții", "night_at_twoam": "În fiecare noapte la 2 dimineața" }, + "invalid_date": "Dată invalidă", + "invalid_date_format": "Format de dată invalid", "invite_people": "Invitați Persoane", "invite_to_album": "Invitați în album", + "ios_debug_info_fetch_ran_at": "Fetch a funcționat la {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizare {dateTime}", + "ios_debug_info_no_processes_queued": "Niciun proces în fundal pus în coadă", + "ios_debug_info_no_sync_yet": "Nicio sarcină de sincronizare în fundal nu a fost încă executată", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces în fundal pus în coadă} other {{count} procese în fundal puse în coadă}}", "ios_debug_info_processing_ran_at": "Procesarea a rulat {dateTime}", "items_count": "{count, plural, one {# element} other{# elemente}}", "jobs": "Sarcini", @@ -1043,6 +1145,9 @@ "kept_this_deleted_others": "S-a păstrat acest material și s-au șters {count, plural, one {# material} other {# materiale}}", "keyboard_shortcuts": "Comenzi rapide de tastatură", "language": "Limbă", + "language_no_results_subtitle": "Încercați să ajustați termenul de căutare", + "language_no_results_title": "Nu au fost găsite limbi", + "language_search_hint": "Căutați limbi...", "language_setting_description": "Selectați limba preferată", "last_seen": "Văzut ultima dată", "latest_version": "Ultima Versiune", @@ -1059,23 +1164,32 @@ "library_page_sort_created": "Data creării", "library_page_sort_last_modified": "Ultima dată modificat", "library_page_sort_title": "Titlu album", + "licenses": "Licențe", "light": "Lumină", "like_deleted": "Preferat șters", "link_motion_video": "Link video în mișcare", - "link_options": "Opțiuni de link", "link_to_oauth": "Link către OAuth", "linked_oauth_account": "Cont OAuth conectat", "list": "Listă", "loading": "Încărcare", "loading_search_results_failed": "Încărcarea rezultatelor căutării nu a reușit", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local": "Local", + "local_asset_cast_failed": "Nu se poate converti un element care nu este încărcat pe server", + "local_assets": "Asset-uri locale", + "local_network": "Rețea locală", + "local_network_sheet_info": "Aplicația se va conecta la server prin intermediul acestei adrese URL atunci când utilizează rețeaua Wi-Fi specificată", + "location_permission": "Permisiunea de locație", + "location_permission_content": "Pentru a utiliza funcția de comutare automată, Immich are nevoie de permisiune pentru locația precisă, astfel încât să poată citi numele rețelei Wi-Fi curente", "location_picker_choose_on_map": "Alege pe hartă", "location_picker_latitude_error": "Introdu o latitudine validă", "location_picker_latitude_hint": "Introdu latitudinea aici", "location_picker_longitude_error": "Introdu o longitudine validă", "location_picker_longitude_hint": "Introdu longitudinea aici", + "lock": "Blocare", + "locked_folder": "Dosar blocat", "log_out": "Deconectare", "log_out_all_devices": "Deconectați-vă de la toate dispozitivele", + "logged_in_as": "Conectat ca {user}", "logged_out_all_devices": "S-au deconectat toate dispozitivele", "logged_out_device": "Dispozitiv deconectat", "login": "Conectare", @@ -1118,8 +1232,7 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bound": "{count} fotografie", - "map_assets_in_bounds": "{count} fotografii", + "map_assets_in_bounds": "{count, plural, one {# poză} other {# poze}}", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1138,13 +1251,21 @@ "map_settings_date_range_option_years": "Ultimii {years} ani", "map_settings_dialog_title": "Setările hărții", "map_settings_include_show_archived": "Include resursele arhivate", + "map_settings_include_show_partners": "Includeți partenerii", "map_settings_only_show_favorites": "Arată doar favorite", "map_settings_theme_settings": "Stilul hărții", "map_zoom_to_see_photos": "Zoom out pentru a vedea fotografii", + "mark_all_as_read": "Marchează toate ca citite", + "mark_as_read": "Marchează ca citit", + "marked_all_as_read": "Marcate toate ca citite", "matches": "Corespunde", "media_type": "Tip media", "memories": "Amintiri", + "memories_all_caught_up": "Sunteți la zi", + "memories_check_back_tomorrow": "Reveniți mâine pentru mai multe amintiri", "memories_setting_description": "Administrați ce vedeți în amintiri", + "memories_start_over": "Începeți de la început", + "memories_swipe_to_close": "Glisează în sus pentru a închide", "memory": "Amintire", "memory_lane_title": "Banda Memoriei {title}", "menu": "Meniu", @@ -1155,9 +1276,19 @@ "merge_people_successfully": "Persoane îmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", + "minute": "Minute", "missing": "Lipsă", + "model": "Model", "month": "Lună", + "monthly_title_text_date_format": "MMMM y", "more": "Mai mult", + "move": "Mută", + "move_off_locked_folder": "Mutați din folderul blocat", + "move_to_lock_folder_action_prompt": "{count} adăugate în dosarul blocat", + "move_to_locked_folder": "Mută în dosarul blocat", + "move_to_locked_folder_confirmation": "Aceste fotografii și videoclipuri vor fi eliminate din toate albumele și vor putea fi vizualizate doar din dosarul blocat", + "moved_to_archive": "Au fost mutate {count, plural, one {# element} other {# elemente}} în arhivă", + "moved_to_library": "Au fost mutate {count, plural, one {# element} other {# elemente}} la bibliotecă", "moved_to_trash": "Mutat în coșul de gunoi", "multiselect_grid_edit_date_time_err_read_only": "Nu se poate edita data fișierului(lor) cu permisiuni doar pentru citire, omitere", "multiselect_grid_edit_gps_err_read_only": "Nu se poate edita locația fișierului(lor) cu permisiuni doar pentru citire, omitere", @@ -1165,11 +1296,15 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclǎ", + "networking_settings": "Rețele", + "networking_subtitle": "Gestionați setările endpoint-ului serverului", "never": "Niciodată", "new_album": "Album Nou", "new_api_key": "Cheie API nouǎ", "new_password": "Parolă nouă", "new_person": "Persoanǎ nouǎ", + "new_pin_code": "Cod PIN nou", + "new_pin_code_subtitle": "Aceasta este prima dată când accesați folderul blocat. Creați un cod PIN pentru a accesa în siguranță această pagină", "new_user_created": "Utilizator nou creat", "new_version_available": "VERSIUNE NOUĂ DISPONIBILĂ", "newest_first": "Cel mai nou primul", @@ -1181,19 +1316,27 @@ "no_albums_yet": "Se pare că nu aveți încă niciun album.", "no_archived_assets_message": "Arhivați fotografii și videoclipuri pentru a le ascunde din vizualizarea fotografii", "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", + "no_assets_to_show": "Nicio resursă de afișat", + "no_cast_devices_found": "Nu s-au găsit dispozitive de difuzare", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", "no_favorites_message": "Adăugați favorite pentru a găsi rapid cele mai bune fotografii și videoclipuri", "no_libraries_message": "Creați o bibliotecă externă pentru a vă vizualiza fotografiile și videoclipurile", + "no_locked_photos_message": "Fotografiile și videoclipurile din folderul blocat sunt ascunse și nu vor apărea atunci când răsfoiți sau căutați în bibliotecă.", "no_name": "Fără Nume", + "no_notifications": "Nicio notificare", + "no_people_found": "Nu au fost găsite persoane potrivite căutării", "no_places": "Nu există locuri", "no_results": "Fără rezultate", "no_results_description": "Încercați un sinonim sau un cuvânt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", + "no_uploads_in_progress": "Nicio încărcare în curs", "not_in_any_album": "Nu există în niciun album", + "not_selected": "Neselectat", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele încărcate anterior, rulați", "notes": "Note", + "nothing_here_yet": "Nimic aici încă", "notification_permission_dialog_content": "Pentru a activa notificările, mergi în Setări > Immich și selectează permite.", "notification_permission_list_tile_content": "Acordă permisiunea pentru a activa notificările.", "notification_permission_list_tile_enable_button": "Activează notificările", @@ -1201,13 +1344,20 @@ "notification_toggle_setting_description": "Activați notificările prin email", "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", + "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", + "offline": "Offline", "ok": "Bine", "oldest_first": "Cel mai vechi mai întâi", + "on_this_device": "Pe acest dispozitiv", "onboarding": "Integrare", - "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setările de administrare.", + "onboarding_locale_description": "Selectați limba preferată. Puteți schimba această opțiune ulterior în setări.", + "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setări.", + "onboarding_server_welcome_description": "Hai să configurăm instanța cu câteva setări comune.", "onboarding_theme_description": "Alegeți o temă de culoare pentru exemplul dvs. Puteți modifica acest lucru mai târziu în setări.", + "onboarding_user_welcome_description": "Hai să începem!", "onboarding_welcome_user": "Bun venit, {user}", + "online": "Online", "only_favorites": "Doar favorite", "open": "Deschide", "open_in_map_view": "Deschideți în vizualizarea hărții", @@ -1216,8 +1366,10 @@ "options": "Opțiuni", "or": "sau", "organize_your_library": "Organizează-ți biblioteca", + "original": "original", "other": "Alte", "other_devices": "Alte dispozitive", + "other_entities": "Alte entități", "other_variables": "Alte variabile", "owned": "Deținut", "owner": "Proprietar", @@ -1225,6 +1377,8 @@ "partner_can_access": "{partner} poate accesa", "partner_can_access_assets": "Toate fotografiile și videoclipurile tale, cu excepția celor din arhivate și sterse", "partner_can_access_location": "Locația în care au fost făcute fotografiile dvs", + "partner_list_user_photos": "Fotografiile lui {user}", + "partner_list_view_all": "Vezi toate", "partner_page_empty_message": "Fotografiile tale nu sunt încă distribuite cu nici un partener.", "partner_page_no_more_users": "Nu mai sunt utilizatori de adăugat", "partner_page_partner_add_failed": "Eșuare adăugare partener", @@ -1259,6 +1413,8 @@ "permanently_delete_assets_prompt": "Sigur doriți să ștergeți definitiv {count, plural, one {această resursă?} other {aceste # resurse?}} Acest lucru va elimina și {count, plural, one {din ea} other {din ele}} album(e).", "permanently_deleted_asset": "Resursă ștearsă definitiv", "permanently_deleted_assets_count": "S-au șters definitiv {count, plural, one {# resursă} other {# resurse}}", + "permission": "Permisiune", + "permission_empty": "Permisiunea dvs. nu trebuie să fie goală", "permission_onboarding_back": "Înapoi", "permission_onboarding_continue_anyway": "Continuă oricum", "permission_onboarding_get_started": "Începe", @@ -1276,6 +1432,10 @@ "photos_count": "{count, plural, one {{count, number} imagine} other{{count, number} imagini}}", "photos_from_previous_years": "Fotografii din anii anteriori", "pick_a_location": "Alegeți o locație", + "pin_code_changed_successfully": "Codul PIN a fost modificat cu succes", + "pin_code_reset_successfully": "Codul PIN a fost resetat cu succes", + "pin_code_setup_successfully": "Configurarea cu succes a unui cod PIN", + "pin_verification": "Verificarea codului PIN", "place": "Loc", "places": "Locații", "places_count": "{count, plural, one {{count, number} Loc} other {{count, number} Locuri}}", @@ -1283,10 +1443,16 @@ "play_memories": "Redare amintiri", "play_motion_photo": "Redare Fotografie în Mișcare", "play_or_pause_video": "Redați sau întrerupeți videoclipul", + "please_auth_to_access": "Vă rugăm să vă autentificați pentru a accesa", + "port": "Port", + "preferences_settings_subtitle": "Gestionați preferințele aplicației", + "preferences_settings_title": "Preferințe", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", "previous_memory": "Memoria anterioară", + "previous_or_next_day": "Zi înainte/înapoi", + "previous_or_next_month": "Lună înainte/înapoi", "previous_or_next_photo": "Fotografie înainte/înapoi", "previous_or_next_year": "An înainte/înapoi", "primary": "Primar", @@ -1323,8 +1489,9 @@ "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la el pentru a-l face cât se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", - "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", + "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_server": "Per server", "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", @@ -1334,6 +1501,7 @@ "purchase_server_description_2": "Statutul de suporter", "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", + "queue_status": "Se pun în coadă {count}/{total}", "rating": "Evaluare cu stele", "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", @@ -1344,10 +1512,13 @@ "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", "reassing_hint": "Atribuiți resursele selectate unei persoane existente", + "recent": "Recent", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", + "recently_taken": "Recent realizate", + "recently_taken_page_title": "Recent realizate", "refresh": "Reîmprospătare", "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "Reîmprospătați fețele", @@ -1359,6 +1530,8 @@ "refreshing_faces": "Se reîmprospătează fețele", "refreshing_metadata": "Se reîmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", + "remote": "De la distanță", + "remote_assets": "Elemente la distanță", "remove": "Eliminați", "remove_assets_album_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din album?", "remove_assets_shared_link_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din acest link comun?", @@ -1366,7 +1539,9 @@ "remove_custom_date_range": "Eliminați intervalul de date personalizat", "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", + "remove_from_album_action_prompt": "{count} șters(e) din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_lock_folder_action_prompt": "{count} șters(e) din dosarul blocat", "remove_from_locked_folder": "Eliminați din folderul securizat", "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile în biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", @@ -1393,19 +1568,27 @@ "reset": "Resetare", "reset_password": "Resetare parolă", "reset_people_visibility": "Resetați vizibilitatea persoanelor", + "reset_pin_code": "Resetare cod PIN", + "reset_sqlite": "Resetare bază de date SQLite", + "reset_sqlite_confirmation": "Sigur doriți să resetați baza de date SQLite? Va trebui să vă deconectați și să vă conectați din nou pentru a resincroniza datele", + "reset_sqlite_success": "Resetarea cu succes a bazei de date SQLite", "reset_to_default": "Resetați la valoarea implicită", "resolve_duplicates": "Rezolvați duplicatele", "resolved_all_duplicates": "Rezolvați toate duplicatele", "restore": "Restaurați", "restore_all": "Restaurați toate", + "restore_trash_action_prompt": "{count} restaurate din gunoi", "restore_user": "Restabiliți utilizatorul", "restored_asset": "Resursă restaurată", "resume": "Reluare", "retry_upload": "Reîncercați încărcarea", "review_duplicates": "Examinați duplicatele", "role": "Rol", + "role_editor": "Editor", "role_viewer": "Vizualizator", + "running": "Rulează", "save": "Salvați", + "save_to_gallery": "Salvați în galerie", "saved_api_key": "Cheie API salvată", "saved_profile": "Profil salvat", "saved_settings": "Setări salvate", @@ -1426,16 +1609,32 @@ "search_camera_model": "Se caută modelul camerei...", "search_city": "Se caută orașul...", "search_country": "Se caută țara...", + "search_filter_apply": "Aplicați filtrul", + "search_filter_camera_title": "Selectați tipul de cameră", + "search_filter_date": "Dată", + "search_filter_date_interval": "{start} la {end}", + "search_filter_date_title": "Selectați un interval de dată", + "search_filter_display_option_not_in_album": "Nu este în album", + "search_filter_display_options": "Opțiuni de afișare", + "search_filter_filename": "Căutare după numele fișierului", + "search_filter_location": "Locaţie", + "search_filter_location_title": "Selectați locația", + "search_filter_media_type": "Tip media", + "search_filter_media_type_title": "Selectați tipul media", + "search_filter_people_title": "Selectați persoane", "search_for": "Căutare după", "search_for_existing_person": "Se caută o persoană existentă", + "search_no_more_result": "Nu mai există rezultate", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", + "search_no_result": "Nu s-au găsit rezultate, încercați un alt termen sau o altă combinație de termeni de căutare", "search_options": "Opțiuni de căutare", "search_page_categories": "Categorii", "search_page_motion_photos": "Fotografii în mișcare", "search_page_no_objects": "Nu sunt informații disponibile despre obiecte", "search_page_no_places": "Nici o informație disponibilă despre locuri", "search_page_screenshots": "Capturi de ecran", + "search_page_search_photos_videos": "Caută fotografiile și videoclipurile tale", "search_page_selfies": "Selfie-uri", "search_page_things": "Obiecte", "search_page_view_all_button": "Vezi toate", @@ -1460,6 +1659,7 @@ "select_album_cover": "Selectați coperta albumului", "select_all": "Selectați tot", "select_all_duplicates": "Selectați toate duplicatele", + "select_all_in": "Selectați tot în {group}", "select_avatar_color": "Selectați culoarea avatarului", "select_face": "Selectați fața", "select_featured_photo": "Selectați fotografia recomandată", @@ -1467,6 +1667,7 @@ "select_keep_all": "Selectați tot pentru păstrare", "select_library_owner": "Selectați proprietarul bibliotecii", "select_new_face": "Selectați o nouǎ fațǎ", + "select_person_to_tag": "Selectați o persoană pentru a o eticheta", "select_photos": "Selectați fotografii", "select_trash_all": "Selectați tot pentru ștergere", "select_user_for_sharing_page_err_album": "Creare album eșuată", @@ -1474,8 +1675,12 @@ "selected_count": "{count, plural, other {# selectat}}", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", + "server_endpoint": "Endpoint server", "server_info_box_app_version": "Versiune Aplicatie", "server_info_box_server_url": "URL-ul server-ului", + "server_offline": "Serverul este offline", + "server_online": "Server online", + "server_privacy": "Confidențialitatea serverului", "server_stats": "Statistici Server", "server_version": "Versiune Server", "set": "Setați", @@ -1485,11 +1690,15 @@ "set_date_of_birth": "Setați data nașterii", "set_profile_picture": "Setați poza de profil", "set_slideshow_to_fullscreen": "Setați Prezentare de Diapozitive la ecran complet", + "set_stack_primary_asset": "Setați ca element principal", "setting_image_viewer_help": "Vizualizatorul detaliilor încarcă mai întâi miniatura mică, apoi încarcă previzualizarea de dimensiune medie (dacă este activată), în cele din urmă încarcă originalul (dacă este activat).", "setting_image_viewer_original_subtitle": "Activează pentru a încărca imaginea originală în rezoluție completă (mare!). Dezactivează pentru a reduce consumul de date (atat pe rețea, cât și în memoria cache a dispozitivului).", "setting_image_viewer_original_title": "Încarcă fotografia originală", "setting_image_viewer_preview_subtitle": "Activează pentru a încărca o imagine în rezoluție medie. Dezactivează pentru a încărca direct imaginea originală sau doar a utiliza miniatura.", "setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare", + "setting_image_viewer_title": "Imagini", + "setting_languages_apply": "Aplică", + "setting_languages_subtitle": "Schimbați limba aplicației", "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup în fundal: {duration}", "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "imediat", @@ -1501,12 +1710,19 @@ "setting_notifications_subtitle": "Ajustează preferințele pentru notificări", "setting_notifications_total_progress_subtitle": "Progresul general al încărcării (resurse finalizate/total)", "setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță în fundal", + "setting_video_viewer_looping_title": "Buclă", + "setting_video_viewer_original_video_subtitle": "Când redați în flux un videoclip de pe server, redați originalul chiar și atunci când este disponibilă o transcodare. Poate duce la încărcare temporară. Videoclipurile disponibile local sunt redate la calitatea originală indiferent de această setare.", + "setting_video_viewer_original_video_title": "Forțează videoclipul original", "settings": "Setări", "settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare", "settings_saved": "Setările au fost salvate", + "setup_pin_code": "Configurați un cod PIN", "share": "Distribuiți", + "share_action_prompt": "{count} elemente partajate", "share_add_photos": "Adaugă fotografii", + "share_assets_selected": "{count} selectat(e)", "share_dialog_preparing": "Se pregătește...", + "share_link": "Partajați linkul", "shared": "Partajat", "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "Dorești să ștergi această activitate?", @@ -1519,6 +1735,7 @@ "shared_by_user": "Partajat de {user}", "shared_by_you": "Partajat de tine", "shared_from_partner": "Fotografii de la {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} încărcate", "shared_link_app_bar_title": "Link-uri distribuite", "shared_link_clipboard_copied_massage": "Copiat în clipboard", "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", @@ -1530,6 +1747,8 @@ "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minut", "shared_link_edit_expire_after_option_minutes": "{count} minute", + "shared_link_edit_expire_after_option_months": "{count} luni", + "shared_link_edit_expire_after_option_year": "{count} an", "shared_link_edit_password_hint": "Introdu parola de distribuire", "shared_link_edit_submit_button": "Actualizează link", "shared_link_error_server_url_fetch": "Nu se poate accesa URL-ul serverului", @@ -1542,11 +1761,14 @@ "shared_link_expires_never": "Expiră ∞", "shared_link_expires_second": "Expiră în {count} secunde", "shared_link_expires_seconds": "Expiră în {count} secunde", + "shared_link_individual_shared": "Partajat individual", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", + "shared_with_me": "Distribuit cu mine", "shared_with_partner": "Partajat cu {partner}", "sharing": "Distribuire", "sharing_enter_password": "Vă rugăm să introduceți parola pentru a vizualiza această pagină.", @@ -1598,6 +1820,7 @@ "sort_title": "Titlu", "source": "Sursă", "stack": "Stivă", + "stack_action_prompt": "{count} suprapuse", "stack_duplicates": "Duplicate stive", "stack_select_one_photo": "Selectați o fotografie principală pentru stivă", "stack_selected_photos": "Fotografie stivă selectată", @@ -1607,14 +1830,17 @@ "start_date": "Data de începere", "state": "Situaţie", "status": "Stare", + "stop_casting": "Opriți difuzarea", "stop_motion_photo": "Opriți Fotografia in Mișcare", "stop_photo_sharing": "Încetați distribuirea fotografiilor?", "stop_photo_sharing_description": "{partner} nu va mai putea accesa fotografiile dvs.", "stop_sharing_photos_with_user": "Nu mai partajați fotografiile cu acest utilizator", "storage": "Spațiu de stocare", "storage_label": "Eticheta de depozitare", + "storage_quota": "Cotă de stocare", "storage_usage": "{used} din {available} utilizați", "submit": "Trimiteți", + "success": "Succes", "suggestions": "Sugestii", "sunrise_on_the_beach": "Rǎsǎrit pe plajǎ", "support": "Suport tehnic", @@ -1622,6 +1848,11 @@ "support_third_party_description": "Instalarea dvs. Immich a fost pregătită de o terță parte. Problemele pe care le întâmpinați pot fi cauzate de acel pachet, așa că vă rugăm să ridicați probleme cu ei în primă instanță utilizând linkurile de mai jos.", "swap_merge_direction": "Schimbați direcția de îmbinare", "sync": "Sincronizare", + "sync_albums": "Sincronizează albumele", + "sync_albums_manual_subtitle": "Sincronizează toate videoclipurile și fotografiile încărcate cu albumele de rezervă selectate", + "sync_local": "Sincronizare locală", + "sync_remote": "Sincronizare la distanță", + "sync_upload_album_setting_subtitle": "Creează și încarcă fotografiile și videoclipurile tale în albumele selectate de pe Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", "tag_created": "Etichetă creată: {tag}", @@ -1631,14 +1862,20 @@ "tag_updated": "Etichetă actualizată: {tag}", "tagged_assets": "Etichetat {count, plural, one {# resursă} other {# resurse}}", "tags": "Etichete", + "tap_to_run_job": "Atingeți pentru a rula job-ul", "template": "Șablon", "theme": "Temă", "theme_selection": "Selectarea temei", "theme_selection_description": "Setați automat tema la mod luminos sau întunecată, în funcție de preferințele de sistem ale browserului dvs", "theme_setting_asset_list_storage_indicator_title": "Arată indicator stocare", "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rând ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicați culoarea primară pe suprafețele de fundal.", + "theme_setting_colorful_interface_title": "Interfață colorată", "theme_setting_image_viewer_quality_subtitle": "Ajustează calitatea detaliilor vizualizatorului de imagine", "theme_setting_image_viewer_quality_title": "Calitate vizualizator de imagine", + "theme_setting_primary_color_subtitle": "Alege o culoare pentru acțiunile și accentele principale.", + "theme_setting_primary_color_title": "Culoare primară", + "theme_setting_system_primary_color_title": "Folosește culoarea sistemului", "theme_setting_system_theme_switch": "Automat (La fel ca setarea sistemului)", "theme_setting_theme_subtitle": "Alege tema aplicației", "theme_setting_three_stage_loading_subtitle": "Încărcarea în trei etape are putea crește performanța încărcării dar generează un volum semnificativ mai mare de trafic pe rețea", @@ -1655,11 +1892,14 @@ "to_parent": "Du-te la părinte", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", + "total": "Total", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", + "trash_action_prompt": "{count} mutat(e) la coșul de gunoi", "trash_all": "Ștergeți Tot", "trash_count": "Ștergeți {count, number}", "trash_delete_asset": "Coș de gunoi/Ștergeți resursa", + "trash_emptied": "Coș de gunoi golit", "trash_no_results_message": "Fotografiile și videoclipurile mutate în coșul de gunoi vor apărea aici.", "trash_page_delete_all": "Șterge tot", "trash_page_empty_trash_dialog_content": "Dorești să golești coșul? Aceste fișiere vor fi șterse permanent din Immich", @@ -1670,9 +1910,14 @@ "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", "type": "Tip", + "unable_to_change_pin_code": "Nu se poate schimba codul PIN", + "unable_to_setup_pin_code": "Nu se poate configura codul PIN", "unarchive": "Dezarhivați", + "unarchive_action_prompt": "{count} șters(e) din Arhivă", "unarchived_count": "{count, plural, other {dezarhivat #}}", + "undo": "Anulează", "unfavorite": "Ștergeți din favorite", + "unfavorite_action_prompt": "{count} șters(e) de la Favorite", "unhide_person": "Dezvăluie persoana", "unknown": "Necunoscut", "unknown_country": "Țară necunoscută", @@ -1688,26 +1933,43 @@ "unsaved_change": "Modificare nesalvată", "unselect_all": "Deselectați toate", "unselect_all_duplicates": "Deselectați toate duplicatele", + "unselect_all_in": "Deselectați toate din {group}", "unstack": "Dezasamblați", + "unstack_action_prompt": "{count} unstacked", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", + "untagged": "Neetichetat", "up_next": "Mai departe", + "updated_at": "Actualizat", "updated_password": "Parolă actualizată", "upload": "Încărcați", + "upload_action_prompt": "{count} în coadă pentru încărcare", "upload_concurrency": "Încărcați simultan", + "upload_details": "Detalii încărcare", "upload_dialog_info": "Vrei să backup resursele selectate pe server?", "upload_dialog_title": "Încarcă resursă", "upload_errors": "Încărcare finalizată cu {count, plural, one {# eroare} other {# erori}}, reîmprospătați pagina pentru a reîncărca noile resurse.", + "upload_finished": "Încărcarea s-a finalizat", "upload_progress": "Rămas {remaining, number} - Procesat {processed, number}/{total, number}", "upload_skipped_duplicates": "Sărit {count, plural, one {# duplicat resursă} other {# duplicate resurse}}", "upload_status_duplicates": "Duplicate", "upload_status_errors": "Erori", "upload_status_uploaded": "Încărcat", "upload_success": "Încărcare reușită, reîmprospătați pagina pentru a vedea resursele noi încărcate.", + "upload_to_immich": "Încărcați pe Immich ({count})", + "uploading": "Se încarcă", + "uploading_media": "Se încarcă fișierele media", + "url": "URL", "usage": "Utilizare", + "use_biometric": "Folosește biometrice", + "use_current_connection": "folosește conexiunea curentă", "use_custom_date_range": "Utilizați în schimb un interval de date personalizat", "user": "Utilizator", + "user_has_been_deleted": "Acest utilizator a fost șters.", "user_id": "ID utilizator", "user_liked": "{user} a apreciat {type, select, photo {această imagine} video {acest video} asset {această resursă} other {it}}", + "user_pin_code_settings": "Cod PIN", + "user_pin_code_settings_description": "Gestionați-vă codul PIN", + "user_privacy": "Confidențialitatea utilizatorilor", "user_purchase_settings": "Cumpărare", "user_purchase_settings_description": "Gestionați-vă achiziția", "user_role_set": "Setați {user} ca {role}", @@ -1716,8 +1978,10 @@ "user_usage_stats_description": "Vedeți statisticile de utilizare a contului", "username": "Nume de utilizator", "users": "Utilizatori", + "users_added_to_album_count": "{count, plural, one {# utilizator a fost adăugat} other {# utilizatori au fost adăugați}} în album", "utilities": "Utilitǎți", "validate": "Validați", + "validate_endpoint_error": "Vă rugăm să introduceți o adresă URL validă", "variables": "Variabile", "version": "Versiune", "version_announcement_closing": "Prietenul tǎu, Alex", @@ -1733,6 +1997,7 @@ "view_album": "Vizualizați Album", "view_all": "Vizualizați Tot", "view_all_users": "Vizulizați toți utilizatorii", + "view_details": "Vedeți detaliile", "view_in_timeline": "Vizualizați în cronologie", "view_link": "Vezi link", "view_links": "Vizualizați scurtǎturi", diff --git a/i18n/ru.json b/i18n/ru.json index c0a01b0be2..921666fb54 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -14,6 +14,7 @@ "add_a_location": "Добавить местоположение", "add_a_name": "Добавить имя", "add_a_title": "Добавить название", + "add_birthday": "Указать дату рождения", "add_endpoint": "Добавить адрес", "add_exclusion_pattern": "Добавить шаблон исключения", "add_import_path": "Добавить путь импорта", @@ -44,6 +45,13 @@ "backup_database": "Создать резервную копию базы данных", "backup_database_enable_description": "Включить создание дампов базы данных", "backup_keep_last_amount": "Количество хранимых резервных копий базы данных", + "backup_onboarding_1_description": "хранение дополнительной внешней копии в облаке или другом физическом месте.", + "backup_onboarding_2_description": "хранение основных файлов и их локальной копии на двух разных типах носителей.", + "backup_onboarding_3_description": "создание трёх копий данных, включая исходные файлы. 2 локальных копии и 1 внешнюю.", + "backup_onboarding_description": "Для надёжной защиты рекомендуется использовать стратегию резервирования данных 3-2-1. Делайте копии как загруженных фотографий и видео, так и базы данных Immich.", + "backup_onboarding_footer": "Дополнительная информация по резервному копированию Immich доступна в документации.", + "backup_onboarding_parts_title": "Стратегия 3-2-1 подразумевает:", + "backup_onboarding_title": "Резервное копирование", "backup_settings": "Настройки резервного копирования базы данных", "backup_settings_description": "Настройки создания резервных копий базы данных.", "cleared_jobs": "Очищены задачи для: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Обложка альбома обновлена", "album_delete_confirmation": "Вы уверены, что хотите удалить альбом {album}?", "album_delete_confirmation_description": "Если альбом был общим, другие пользователи больше не смогут получить к нему доступ.", + "album_deleted": "Альбом удалён", "album_info_card_backup_album_excluded": "ИСКЛЮЧЕН", "album_info_card_backup_album_included": "ВКЛЮЧЕН", "album_info_updated": "Информация об альбоме обновлена", @@ -510,6 +519,7 @@ "back_close_deselect": "Назад, закрыть или отменить выбор", "background_location_permission": "Доступ к местоположению в фоне", "background_location_permission_content": "Чтобы считывать имя Wi-Fi сети в фоне, приложению *всегда* необходим доступ к точному местоположению устройства", + "backup": "Резервное копирование", "backup_album_selection_page_albums_device": "Альбомы на устройстве ({count})", "backup_album_selection_page_albums_tap": "Нажмите, чтобы включить, дважды, чтобы исключить", "backup_album_selection_page_assets_scatter": "Ваши изображения и видео могут находиться в разных альбомах. Вы можете выбрать, какие альбомы включить, а какие исключить из резервного копирования.", @@ -582,7 +592,7 @@ "birthdate_saved": "Дата рождения успешно сохранена", "birthdate_set_description": "Дата рождения используется для определения возраста человека на момент фотографии.", "blurred_background": "Размытый фон", - "bugs_and_feature_requests": "Ошибки и предложения", + "bugs_and_feature_requests": "Ошибки и запросы", "build": "Сборка", "build_image": "Версия сборки", "bulk_delete_duplicates_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# дублирующийся объект} many {# дублирующихся объектов} other {# дублирующихся объекта}}? Будет сохранён самый большой файл в каждой группе, а его дубликаты навсегда удалены. Это действие нельзя отменить!", @@ -723,6 +733,7 @@ "current_server_address": "Текущий адрес сервера", "custom_locale": "Пользовательский регион", "custom_locale_description": "Форматирование дат и чисел в зависимости от языка и региона", + "custom_url": "Свой URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", @@ -742,7 +753,8 @@ "default_locale": "Дата и время по умолчанию", "default_locale_description": "Использовать формат даты и времени в соответствии с языковым стандартом вашего браузера", "delete": "Удалить", - "delete_action_prompt": "{count} удалено навсегда", + "delete_action_confirmation_message": "Вы действительно хотите удалить этот объект? Это действие переместит объект в корзину сервера и предложит удалить его локально.", + "delete_action_prompt": "{count} удалено", "delete_album": "Удалить альбом", "delete_api_key_prompt": "Вы действительно хотите удалить этот API ключ?", "delete_dialog_alert": "Эти элементы будут безвозвратно удалены с сервера, а также с вашего устройства", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии", "delete_local_dialog_ok_force": "Все равно удалить", "delete_others": "Удалить остальные", + "delete_permanently": "Удалить навсегда", + "delete_permanently_action_prompt": "{count} удалено навсегда", "delete_shared_link": "Удалить публичную ссылку", "delete_shared_link_dialog_title": "Удалить публичную ссылку", "delete_tag": "Удалить тег", @@ -815,6 +829,7 @@ "edit": "Редактировать", "edit_album": "Редактировать альбом", "edit_avatar": "Изменить аватар", + "edit_birthday": "Изменить дату рождения", "edit_date": "редактировать дату", "edit_date_and_time": "редактировать дату и время", "edit_description": "Изменить описание", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Добавить описание...", + "exif_bottom_sheet_description_error": "Не удалось обновить описание", "exif_bottom_sheet_details": "ПОДРОБНОСТИ", "exif_bottom_sheet_location": "МЕСТО", "exif_bottom_sheet_people": "ЛЮДИ", @@ -1002,6 +1018,8 @@ "explorer": "Проводник", "export": "Экспортировать", "export_as_json": "Экспорт в JSON", + "export_database": "Экспорт базы данных", + "export_database_description": "Экспорт базы данных SQLite", "extension": "Расширение", "external": "Внешний", "external_libraries": "Внешние библиотеки", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Языков не найдено", "language_search_hint": "Поиск языков...", "language_setting_description": "Выберите предпочитаемый вами язык", + "large_files": "Файлы наибольшего размера", "last_seen": "Последний доступ", "latest_version": "Последняя версия", "latitude": "Широта", @@ -1165,7 +1184,6 @@ "light": "Светлая", "like_deleted": "Лайк удален", "link_motion_video": "Ссылка на движущееся видео", - "link_options": "Настройки ссылки", "link_to_oauth": "Присоединение к OAuth", "linked_oauth_account": "Присоединённый аккаунт OAuth", "list": "Список", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Управление устройствами, на которых вы входили в свой аккаунт", "manage_your_oauth_connection": "Настройки подключённого OAuth", "map": "Карта", - "map_assets_in_bound": "{count} фото", - "map_assets_in_bounds": "{count} фото", + "map_assets_in_bounds": "{count, plural, one {# фото} other {# фото}}", "map_cannot_get_user_location": "Невозможно получить местоположение пользователя", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "Это местоположение", @@ -1295,6 +1312,8 @@ "my_albums": "Мои альбомы", "name": "Имя", "name_or_nickname": "Имя или ник", + "network_requirement_photos_upload": "Использовать мобильный интернет для загрузки фото", + "network_requirement_videos_upload": "Использовать мобильный интернет для загрузки видео", "networking_settings": "Сеть", "networking_subtitle": "Настройка подключения к серверу", "never": "никогда", @@ -1565,7 +1584,7 @@ "require_user_to_change_password_on_first_login": "Требовать у пользователя сменить пароль при первом входе", "rescan": "Повторное сканирование", "reset": "Сброс", - "reset_password": "Сброс пароля", + "reset_password": "Сбросить пароль", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", "reset_sqlite": "Очистить базу данных SQLite", @@ -1581,7 +1600,8 @@ "restored_asset": "Восстановленный объект", "resume": "Продолжить", "retry_upload": "Повторить загрузку", - "review_duplicates": "Разобрать дубликаты", + "review_duplicates": "Разбор дубликатов", + "review_large_files": "Обзор больших файлов", "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", @@ -1662,7 +1682,7 @@ "select_avatar_color": "Выберите цвет аватара", "select_face": "Выбрать лицо", "select_featured_photo": "Выбрать избранное фото", - "select_from_computer": "Выберите с компьютера", + "select_from_computer": "Выбрать с компьютера", "select_keep_all": "Выбрать все для сохранения", "select_library_owner": "Выберите владельца библиотеки", "select_new_face": "Выбрать другое лицо", @@ -1739,6 +1759,7 @@ "shared_link_clipboard_copied_massage": "Скопировано в буфер обмена", "shared_link_clipboard_text": "Ссылка: {link}\nПароль: {password}", "shared_link_create_error": "Ошибка при создании публичной ссылки", + "shared_link_custom_url_description": "Доступ к этой общей ссылке с помощью заданного пользователем URL-адреса", "shared_link_edit_description_hint": "Введите описание публичного доступа", "shared_link_edit_expire_after_option_day": "1 день", "shared_link_edit_expire_after_option_days": "{count} дней", @@ -1764,6 +1785,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Управление публичными ссылками", "shared_link_options": "Параметры публичных ссылок", + "shared_link_password_description": "Требовать пароль для доступа к этой общей ссылке", "shared_links": "Публичные ссылки", "shared_links_description": "Делитесь фотографиями и видео по ссылке", "shared_photos_and_videos_count": "{assetCount, plural, other {# фото и видео.}}", @@ -1941,11 +1963,13 @@ "updated_at": "Обновлён", "updated_password": "Пароль изменён", "upload": "Загрузить", + "upload_action_prompt": "{count} ожидают загрузки", "upload_concurrency": "Параллельность загрузки", "upload_details": "Подробности загрузки", "upload_dialog_info": "Хотите загрузить выбранные объекты на сервер?", "upload_dialog_title": "Загрузить объект", "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные объекты.", + "upload_finished": "Загрузка завершена", "upload_progress": "Осталось {remaining, number} - Обработано {processed, number}/{total, number}", "upload_skipped_duplicates": "Пропущен{count, plural, one { # дубликат} many {о # дубликатов} other {о # дубликата}}", "upload_status_duplicates": "Дубликаты", @@ -1954,6 +1978,7 @@ "upload_success": "Загрузка прошла успешно. Обновите страницу, чтобы увидеть новые объекты.", "upload_to_immich": "Загрузка в Immich ({count})", "uploading": "Загружается", + "uploading_media": "Выполняется загрузка", "url": "URL", "usage": "Использование", "use_biometric": "Использовать биометрию", @@ -1990,7 +2015,7 @@ "videos": "Видео", "videos_count": "{count, plural, one {# видео} other {# видео}}", "view": "Просмотр", - "view_album": "Просмотреть альбом", + "view_album": "Открыть альбом", "view_all": "Посмотреть всё", "view_all_users": "Показать всех пользователей", "view_details": "Посмотреть подробности", diff --git a/i18n/sk.json b/i18n/sk.json index a29262a6e8..a5c4c08af9 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,7 @@ "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", + "add_birthday": "Pridať narodeniny", "add_endpoint": "Pridať koncový bod", "add_exclusion_pattern": "Pridať vzor vylúčenia", "add_import_path": "Pridať cestu pre import", @@ -44,6 +45,13 @@ "backup_database": "Vytvoriť výpis databázy", "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", + "backup_onboarding_1_description": "externú kópiu v cloude alebo na inom fyzickom mieste.", + "backup_onboarding_2_description": "lokálne kópie na rôznych zariadeniach. To zahŕňa hlavné súbory a ich lokálnu zálohu.", + "backup_onboarding_3_description": "celkový počet kópií vašich údajov vrátane pôvodných súborov. Toto zahŕňa 1 externú kópiu a 2 lokálne kópie.", + "backup_onboarding_description": "Na ochranu vašich údajov sa odporúča stratégia zálohovania 3-2-1. Pre komplexné riešenie zálohovania by ste mali uchovávať kópie nahratých fotografií/videí, ako aj databázy Immich.", + "backup_onboarding_footer": "Ďalšie informácie o vytváraní zálohy Immich nájdete v dokumentácii.", + "backup_onboarding_parts_title": "Zálohovanie 3-2-1 zahŕňa:", + "backup_onboarding_title": "Zálohy", "backup_settings": "Nastavenia výpisu databázy", "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", @@ -240,8 +248,8 @@ "send_welcome_email": "Odoslať uvítací e-mail", "server_external_domain_settings": "Externá doména", "server_external_domain_settings_description": "Verejná doména pre zdieľané odkazy, vrátane http(s)://", - "server_public_users": "Verejní užívatelia", - "server_public_users_description": "Všetci užívatelia (meno a email) sú uvedení pri pridávaní užívateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam užívateľov bude dostupný iba správcom.", + "server_public_users": "Verejní používatelia", + "server_public_users_description": "Všetci používatelia (meno a email) sú uvedení pri pridávaní používateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam používateľov bude dostupný iba správcom.", "server_settings": "Server", "server_settings_description": "Spravovať nastavenia servera", "server_welcome_message": "Uvítacia správa", @@ -264,16 +272,16 @@ "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", "storage_template_settings_description": "Spravujte štruktúru priečinkov a názov súboru odovzdaného média", - "storage_template_user_label": "{label} je Štítok úložiska používateľa", + "storage_template_user_label": "{label} je štítok úložiska používateľa", "system_settings": "Nastavenia systému", "tag_cleanup_job": "Prečistenie štítkov", "template_email_available_tags": "V šablóne môžete použiť nasledujúce premenné: {tags}", - "template_email_if_empty": "Ak nie je zadaná žiadna šablóna, bude použitá predvolená šablóna.", + "template_email_if_empty": "Ak je šablóna prázdna, použije sa predvolený e-mail.", "template_email_invite_album": "Šablóna Pozvánky do albumu", "template_email_preview": "Ukážka", "template_email_settings": "Emailové šablóny", "template_email_update_album": "Upraviť šablónu albumu", - "template_email_welcome": "Šablóna uvítajúceho emailu", + "template_email_welcome": "Šablóna uvítacieho e-mailu", "template_settings": "Šablóna upozornení", "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", @@ -347,19 +355,19 @@ "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", "trash_settings": "Kôš", "trash_settings_description": "Spravovať nastavenia koša", - "user_cleanup_job": "Premazanie používateľov", + "user_cleanup_job": "Prečistenie používateľov", "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", "user_delete_delay_settings": "Oneskorenie vymazania", "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", - "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", + "user_delete_immediately_checkbox": "Zaradiť používateľa a položky do poradia na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", "user_management": "Spravovanie používateľov", "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", - "user_settings": "Používateľ", + "user_settings": "Nastavenia používateľa", "user_settings_description": "Spravovať používateľské nastavenia", "user_successfully_removed": "Používateľ {email} bol úspešne odstránený.", "version_check_enabled_description": "Povoliť kontrolu verzie", @@ -397,6 +405,7 @@ "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", + "album_deleted": "Album bol vymazaný", "album_info_card_backup_album_excluded": "VYLÚČENÉ", "album_info_card_backup_album_included": "ZAHRNUTÉ", "album_info_updated": "Informácie albumu aktualizované", @@ -510,6 +519,7 @@ "back_close_deselect": "Späť, zavrieť alebo zrušiť výber", "background_location_permission": "Povolenie na určenie polohy na pozadí", "background_location_permission_content": "Aby bolo možné prepínať siete pri spustení na pozadí, musí mať aplikácia Immich *vždy* presný prístup k polohe, aby mohla prečítať názov siete Wi-Fi", + "backup": "Zálohovanie", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na položku ju zahrniete, dvojitým ťuknutím ju vylúčite", "backup_album_selection_page_assets_scatter": "Súbory môžu byť roztrúsené vo viacerých albumoch. To umožňuje zahrnúť alebo vylúčiť albumy počas procesu zálohovania.", @@ -721,8 +731,9 @@ "current_device": "Súčasné zariadenie", "current_pin_code": "Aktuálny PIN kód", "current_server_address": "Aktuálna adresa servera", - "custom_locale": "Vlastná Lokalizácia", + "custom_locale": "Vlastné nastavenie jazyka", "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", + "custom_url": "Vlastná URL adresa", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavá", @@ -742,7 +753,8 @@ "default_locale": "Predvolené miestne nastavenie", "default_locale_description": "Formátovanie dátumov a čísel na základe miestneho nastavenia prehliadača", "delete": "Vymazať", - "delete_action_prompt": "{count} natrvalo vymazaných", + "delete_action_confirmation_message": "Naozaj chcete túto položku odstrániť? Táto akcia presunie položku do koša na serveri a zobrazí sa otázka, či ju chcete odstrániť aj lokálne", + "delete_action_prompt": "{count} vymazaných", "delete_album": "Odstrániť album", "delete_api_key_prompt": "Naozaj chcete odstrániť tento API kľúč?", "delete_dialog_alert": "Tieto položky budú natrvalo odstránené z aplikácie Immich a z vášho zariadenia", @@ -760,13 +772,15 @@ "delete_local_dialog_ok_backed_up_only": "Vymazať len zálohované", "delete_local_dialog_ok_force": "Napriek tomu vymazať", "delete_others": "Vymazať ostatné", + "delete_permanently": "Natrvalo odstrániť", + "delete_permanently_action_prompt": "{count} natrvalo odstránených", "delete_shared_link": "Odstrániť zdieľaný odkaz", "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", "delete_tag": "Odstrániť štítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrániť štítok menom {tagName}?", "delete_user": "Vymazať používateľa", "deleted_shared_link": "Vymazaný zdieľaný odkaz", - "deletes_missing_assets": "Chýbajú vymazané položky z disku", + "deletes_missing_assets": "Odstráni položky chýbajúce na disku", "description": "Popis", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", @@ -774,7 +788,7 @@ "details": "Podrobnosti", "direction": "Smer", "disabled": "Vypnuté", - "disallow_edits": "Zakázať editovanie", + "disallow_edits": "Zakázať úpravy", "discord": "Discord", "discover": "Objaviť", "discovered_devices": "Objavené zariadenia", @@ -814,7 +828,8 @@ "duration": "Trvanie", "edit": "Upraviť", "edit_album": "Upraviť album", - "edit_avatar": "Upraviť avatar", + "edit_avatar": "Upraviť profilový obrázok", + "edit_birthday": "Upraviť narodeniny", "edit_date": "Upraviť dátum", "edit_date_and_time": "Upraviť dátum a čas", "edit_description": "Upraviť popis", @@ -838,7 +853,7 @@ "editor_close_without_save_prompt": "Úpravy nebudú uložené", "editor_close_without_save_title": "Zavrieť editor?", "editor_crop_tool_h2_aspect_ratios": "Pomer strán", - "editor_crop_tool_h2_rotation": "Rotovanie", + "editor_crop_tool_h2_rotation": "Otočenie", "email": "E-mail", "email_notifications": "E-mailové oznámenia", "empty_folder": "Tento priečinok je prázdny", @@ -861,23 +876,23 @@ "error_tag_face_bounding_box": "Chyba pri označovaní tváre - nemožno získať súradnice ohraničujúceho poľa", "error_title": "Chyba - niečo sa pokazilo", "errors": { - "cannot_navigate_next_asset": "Nedokážem prejsť na ďaľšiu položku", - "cannot_navigate_previous_asset": "Nedokážem prejsť na predošlú položku", - "cant_apply_changes": "Nedokážem aplikovať zmeny", + "cannot_navigate_next_asset": "Nie je možné prejsť na ďalšiu položku", + "cannot_navigate_previous_asset": "Nie je možné prejsť na predošlú položku", + "cant_apply_changes": "Nie je možné použiť zmeny", "cant_change_activity": "Nie je možné {enabled, select, true {zakázať} other {povoliť}} aktivitu", - "cant_change_asset_favorite": "Nedokážem zmeniť obľúbenosť pre položku", + "cant_change_asset_favorite": "Nie je možné zmeniť stav obľúbenosti pre položku", "cant_change_metadata_assets_count": "Nie je možné zmeniť metadáta pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "cant_get_faces": "Nedokážem získať tváre", "cant_get_number_of_comments": "Nedokážem získať počet komentárov", "cant_search_people": "Nedokážem hľadať osoby", "cant_search_places": "Nedokážem hľadať miesta", "error_adding_assets_to_album": "Nepodarilo sa pridať položky do albumu", - "error_adding_users_to_album": "Nepodarilo sa pridať užívateľov do albumu", - "error_deleting_shared_user": "Nepodarilo sa odstrániť zdieľaného používateľa", + "error_adding_users_to_album": "Nepodarilo sa pridať používateľov do albumu", + "error_deleting_shared_user": "Chyba pri odstraňovaní zdieľaného používateľa", "error_downloading": "Nepodarilo sa stiahnuť {filename}", "error_hiding_buy_button": "Nepodarilo sa skryť tlačidlo kúpiť", "error_removing_assets_from_album": "Nepodarilo sa odstrániť položku z albumu, podrobnejšie informácie nájdete v konzole", - "error_selecting_all_assets": "Nepodarilo sa vybrať položky", + "error_selecting_all_assets": "Chyba pri výbere všetkých položiek", "exclusion_pattern_already_exists": "Tento vzor vylúčenia už existuje.", "failed_to_create_album": "Nepodarilo sa vytvoriť album", "failed_to_create_shared_link": "Nepodarilo sa vytvoriť zdieľaný odkaz", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Pridať popis...", + "exif_bottom_sheet_description_error": "Chyba pri aktualizácii popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ĽUDIA", @@ -1002,6 +1018,8 @@ "explorer": "Prieskumník", "export": "Exportovať", "export_as_json": "Exportovať do JSON", + "export_database": "Exportovať databázu", + "export_database_description": "Exportovať databázu SQLite", "extension": "Rozšírenie", "external": "Externý", "external_libraries": "Externé knižnice", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Neboli nájdené žiadne jazyky", "language_search_hint": "Vyhľadať jazyky...", "language_setting_description": "Vyberte požadovaný jazyk", + "large_files": "Veľké súbory", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1165,7 +1184,6 @@ "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Prepojiť s OAuth", "linked_oauth_account": "Pripojený OAuth účet", "list": "Zoznam", @@ -1205,7 +1223,7 @@ "login_form_failed_get_oauth_server_config": "Chyba prihlásenia pomocou OAuth, skontrolujte adresu URL servera", "login_form_failed_get_oauth_server_disable": "Funkcia OAuth nie je na tomto serveri dostupná", "login_form_failed_login": "Chyba prihlásenia, skontrolujte url adresu servera, e-mail a heslo", - "login_form_handshake_exception": "Nastala chyba handshake. Zapnite podoporu samo-podpísaných certifikátov v nastaveniach ak používate samo-podpísané certifikáty.", + "login_form_handshake_exception": "Došlo k výnimke Handshake so serverom. Ak používate certifikát s vlastným podpisom, povoľte v nastaveniach podporu certifikátov s vlastným podpisom.", "login_form_password_hint": "heslo", "login_form_save_login": "Zostať prihlásený", "login_form_server_empty": "Zadajte URL adresu servera.", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Spravovať vaše prihlásené zariadenia", "manage_your_oauth_connection": "Spravovať vaše OAuth spojenia", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotiek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few {# fotky} other {# fotiek}}", "map_cannot_get_user_location": "Nie je možné získať polohu používateľa", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "Použiť túto polohu", @@ -1306,7 +1323,7 @@ "new_pin_code_subtitle": "Toto je váš prvý prístup k zamknutému priečinku. Vytvorte si PIN kód na bezpečný prístup k tejto stránke", "new_user_created": "Nový používateľ vytvorený", "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", - "newest_first": "Najnovšie prvé", + "newest_first": "Najprv najnovšie", "next": "Ďalej", "next_memory": "Ďalšia spomienka", "no": "Nie", @@ -1347,7 +1364,7 @@ "official_immich_resources": "Oficiálne Immich zdroje", "offline": "Offline", "ok": "OK", - "oldest_first": "Najstaršie prvé", + "oldest_first": "Najprv najstaršie", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", @@ -1379,7 +1396,7 @@ "partner_list_user_photos": "Fotky používateľa {user}", "partner_list_view_all": "Zobraziť všetky", "partner_page_empty_message": "Vaše fotky zatiaľ nie sú zdieľané so žiadnym partnerom.", - "partner_page_no_more_users": "Žiadni ďalší užívatelia na zdieľanie", + "partner_page_no_more_users": "Žiadni ďalší používatelia na pridanie", "partner_page_partner_add_failed": "Pridávanie partnera zlyhalo", "partner_page_select_partner": "Zvoliť partnera", "partner_page_shared_to_title": "Zdieľané pre", @@ -1469,7 +1486,7 @@ "public_album": "Verejný album", "public_share": "Verejné zdieľanie", "purchase_account_info": "Podporovateľ", - "purchase_activated_subtitle": "Ďakujeme za podporu Immich a softvéru s otvorenými zdrojákmi", + "purchase_activated_subtitle": "Ďakujeme vám za podporu aplikácie Immich a softvéru s otvoreným zdrojovým kódom", "purchase_activated_time": "Aktivované {date}", "purchase_activated_title": "Váš kľúč je úspešne aktivovaný", "purchase_button_activate": "Aktivovať", @@ -1487,8 +1504,8 @@ "purchase_license_subtitle": "Kúpte si Immich a podporte neustály vývoj tejto služby", "purchase_lifetime_description": "Doživotná platnosť", "purchase_option_title": "MOŽNOSTI NÁKUPU", - "purchase_panel_info_1": "Vývoj Immich zaberá veľa času a úsilia, a máme zamestnaných fulltime inžinierov, aby ho spravili ako sa najlepšie dá. Naša misia je, aby sa open-source softvér a etické biznis praktiky stali udržateľným zdrojom príjmu pre vývojárov a vytvorili ekosystém rešpektujúci súkromie so skutočnými náhradami voči zneužívajúcim cloudovým službám.", - "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne prídavné funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", + "purchase_panel_info_1": "Vývoj aplikácie Immich zaberá veľa času a úsilia, pričom na ňom pracujú inžinieri na plný úväzok, aby bol čo najlepší. Naším poslaním je, aby sa softvér s otvoreným zdrojovým kódom a etické obchodné postupy stali udržateľným zdrojom príjmov pre vývojárov a aby sme vytvorili ekosystém rešpektujúci súkromie so skutočnými alternatívami k zneužívajúcim cloudovým službám.", + "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne pridané funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", "purchase_panel_title": "Podporiť projekt", "purchase_per_server": "Za server", "purchase_per_user": "Za používateľa", @@ -1575,13 +1592,14 @@ "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", - "restore_all": "Navrátit všetko", + "restore_all": "Navrátiť všetko", "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", - "restored_asset": "Navrátené položky", + "restored_asset": "Navrátená položka", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", "review_duplicates": "Preskúmať duplikáty", + "review_large_files": "Skontrolovať veľké súbory", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", @@ -1726,7 +1744,7 @@ "shared_album_activities_input_disable": "Komentár je zakázaný", "shared_album_activity_remove_content": "Chcete vymazať túto aktivitu?", "shared_album_activity_remove_title": "Vymazať aktivitu", - "shared_album_section_people_action_error": "Vyskytla sa chyba pri odchádzaní / odstraňovaní používateľa z albumu", + "shared_album_section_people_action_error": "Vyskytla sa chyba pri opustení/odobratí z albumu", "shared_album_section_people_action_leave": "Odstrániť používateľa z albumu", "shared_album_section_people_action_remove_user": "Odstrániť používateľa z albumu", "shared_album_section_people_title": "ĽUDIA", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Skopírované do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Vyskytla sa chyba behom vytvárania zdieľaného odkazu", + "shared_link_custom_url_description": "Prístup k tomuto zdieľanému odkazu pomocou vlastnej URL adresy", "shared_link_edit_description_hint": "Zadajte popis zdieľania", "shared_link_edit_expire_after_option_day": "1 deň", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovať zdieľané odkazy", "shared_link_options": "Možnosti zdieľaných odkazov", + "shared_link_password_description": "Vyžadovať heslo pre prístup k tomuto zdieľanému odkazu", "shared_links": "Zdieľané odkazy", "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", @@ -1833,7 +1853,7 @@ "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", - "stop_sharing_photos_with_user": "Zastaviť zdieľanie týchto fotiek s týmto používateľom", + "stop_sharing_photos_with_user": "Zastaviť zdieľanie vašich fotiek s týmto používateľom", "storage": "Ukladací priestor", "storage_label": "Štítok úložiska", "storage_quota": "Úložný limit", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualizované", "updated_password": "Heslo zmenené", "upload": "Nahrať", + "upload_action_prompt": "{count} v poradí na nahratie", "upload_concurrency": "Súbežnosť nahrávania", "upload_details": "Podrobnosti o nahrávaní", "upload_dialog_info": "Chcete zálohovať zvolené médiá na server?", "upload_dialog_title": "Nahrať médiá", "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku, aby sa zobrazili nové položky.", + "upload_finished": "Nahrávanie dokončené", "upload_progress": "Ostáva {remaining, number} - Spracovaných {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicitná položka} few {Preskočené # duplicitné položky} other {Preskočených # duplicitných položiek}}", "upload_status_duplicates": "Duplikáty", @@ -1954,6 +1976,7 @@ "upload_success": "Nahrávanie úspešné, pridané súbory sa zobrazia po obnovení stránky.", "upload_to_immich": "Nahrať na Immich ({count})", "uploading": "Nahrávanie", + "uploading_media": "Nahrávanie médií", "url": "Odkaz URL", "usage": "Použitie", "use_biometric": "Použiť biometrické údaje", @@ -1961,7 +1984,7 @@ "use_custom_date_range": "Použiť radšej vlastný rozsah dátumov", "user": "Používateľ", "user_has_been_deleted": "Tento používateľ bol vymazaný.", - "user_id": "Používateľské ID", + "user_id": "ID používateľa", "user_liked": "Používateľovi {user} sa páči {type, select, photo {táto fotka} video {toto video} asset {táto položka} other {toto}}", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "Spravujte svoj PIN kód", @@ -1981,7 +2004,7 @@ "variables": "Premenné", "version": "Verzia", "version_announcement_closing": "Tvoj kamarát, Alex", - "version_announcement_message": "Ahoj! Nová verzia Immich je dostupná. Prosím prečítajte si poznámky k vydaniu, aby ste sa uistili, že inštalácia bude aktuálna bez problémov, najmä ak používate WatchTower alebo akýkoľvek spôsob automatickej aktualizácie Immich servera.", + "version_announcement_message": "Ahoj! K dispozícii je nová verzia aplikácie Immich. Prosím, venujte trochu času prečítaniu poznámok k vydaniu, aby ste sa uistili, že vaša inštalácia je aktuálna a predišli tak akýmkoľvek chybám v konfigurácii, najmä ak používate WatchTower alebo akýkoľvek mechanizmus, ktorý sa stará o automatickú aktualizáciu vašej inštancie Immich.", "version_history": "História verzií", "version_history_item": "Inštalovaná {version} dňa {date}", "video": "Video", diff --git a/i18n/sl.json b/i18n/sl.json index e08a9dd506..ad67e2f5c4 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -14,6 +14,7 @@ "add_a_location": "Dodaj lokacijo", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rojstni dan", "add_endpoint": "Dodaj končno točko", "add_exclusion_pattern": "Dodaj vzorec izključitve", "add_import_path": "Dodaj pot uvoza", @@ -44,6 +45,13 @@ "backup_database": "Ustvari izpis baze podatkov", "backup_database_enable_description": "Omogoči izpise baze podatkov", "backup_keep_last_amount": "Število prejšnjih odlagališč, ki jih je treba obdržati", + "backup_onboarding_1_description": "kopijo zunaj lokacije v oblaku ali na drugi fizični lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različnih napravah. To vključuje glavne datoteke in lokalno varnostno kopijo teh datotek.", + "backup_onboarding_3_description": "skupno število kopij vaših podatkov, vključno z izvirnimi datotekami. To vključuje 1 kopijo zunaj lokacije in 2 lokalni kopiji.", + "backup_onboarding_description": "Za zaščito podatkov priporočamo strategijo varnostnega kopiranja 3-2-1. Za celovito rešitev varnostnega kopiranja hranite kopije naloženih fotografij/videoposnetkov in podatkovne baze Immich.", + "backup_onboarding_footer": "Za več informacij o varnostnem kopiranju Immicha glejte dokumentacijo.", + "backup_onboarding_parts_title": "Varnostna kopija 3-2-1 vključuje:", + "backup_onboarding_title": "Varnostne kopije", "backup_settings": "Nastavitve izpisa baze podatkov", "backup_settings_description": "Upravljanje nastavitev izpisa podatkovne baze.", "cleared_jobs": "Razčiščeno opravilo za: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Naslovnica albuma posodobljena", "album_delete_confirmation": "Ali ste prepričani, da želite izbrisati album {album}?", "album_delete_confirmation_description": "Če je ta album v skupni rabi, drugi uporabniki ne bodo mogli več dostopati do njega.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZKLJUČENO", "album_info_card_backup_album_included": "VKLJUČENO", "album_info_updated": "Podatki o albumu posodobljeni", @@ -510,6 +519,7 @@ "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", "background_location_permission_content": "Ko deluje v ozadju mora imeti Immich za zamenjavo omrežij, *vedno* dostop do natančne lokacije, da lahko aplikacija prebere ime omrežja Wi-Fi", + "backup": "Varnostna kopija", "backup_album_selection_page_albums_device": "Albumi v napravi ({count})", "backup_album_selection_page_albums_tap": "Tapnite za vključitev, dvakrat tapnite za izključitev", "backup_album_selection_page_assets_scatter": "Sredstva so lahko razpršena po več albumih. Tako je mogoče med postopkom varnostnega kopiranja albume vključiti ali izključiti.", @@ -723,6 +733,7 @@ "current_server_address": "Trenutni naslov strežnika", "custom_locale": "Jezik po meri", "custom_locale_description": "Oblikujte datume in številke glede na jezik in regijo", + "custom_url": "URL po meri", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", @@ -742,7 +753,8 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in številke glede na lokalne nastavitve brskalnika", "delete": "Izbriši", - "delete_action_prompt": "trajno izbrisano {count}", + "delete_action_confirmation_message": "Ali ste prepričani, da želite izbrisati to sredstvo? S tem dejanjem boste sredstvo premaknili v koš na strežniku in vas pozvali, ali ga želite izbrisati lokalno", + "delete_action_prompt": "izbrisano {count}", "delete_album": "Izbriši album", "delete_api_key_prompt": "Ali ste prepričani, da želite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaše naprave", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Izbriši samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriši", "delete_others": "Izbriši ostale", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši povezavo skupne rabe", "delete_shared_link_dialog_title": "Izbriši povezavo skupne rabe", "delete_tag": "Izbriši oznako", @@ -815,6 +829,7 @@ "edit": "Uredi", "edit_album": "Uredi album", "edit_avatar": "Uredi avatar", + "edit_birthday": "Uredi rojstni dan", "edit_date": "Uredi datum", "edit_date_and_time": "Uredi datum in uro", "edit_description": "Uredi opis", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis..", + "exif_bottom_sheet_description_error": "Napaka pri posodabljanju opisa", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", @@ -1002,6 +1018,8 @@ "explorer": "Raziskovalec", "export": "Izvoz", "export_as_json": "Izvozi kot JSON", + "export_database": "Izvoz baze podatkov", + "export_database_description": "Izvozite bazo podatkov SQLite", "extension": "Razširitev", "external": "Zunanji", "external_libraries": "Zunanje knjižnice", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Ni najdenih jezikov", "language_search_hint": "Iskanje jezikov...", "language_setting_description": "Izberite želeni jezik", + "large_files": "Velike datoteke", "last_seen": "Nazadnje viden", "latest_version": "Najnovejša različica", "latitude": "Zemljepisna širina", @@ -1165,7 +1184,6 @@ "light": "Svetlo", "like_deleted": "Všeček izbrisan", "link_motion_video": "Povezava videa gibanja", - "link_options": "Možnosti povezave", "link_to_oauth": "Povezava do OAuth", "linked_oauth_account": "Povezan račun OAuth", "list": "Seznam", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{count} slika", - "map_assets_in_bounds": "{count} slik", + "map_assets_in_bounds": "{count, plural, one {# slika} other {# slik}}", "map_cannot_get_user_location": "Lokacije uporabnika ni mogoče dobiti", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Uporabi to lokacijo", @@ -1582,6 +1599,7 @@ "resume": "Nadaljuj", "retry_upload": "Poskusite znova naložiti", "review_duplicates": "Pregled dvojnikov", + "review_large_files": "Pregled velikih datotek", "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopirano v odložišče", "shared_link_clipboard_text": "Povezava: {link}\nGeslo: {password}", "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", + "shared_link_custom_url_description": "Dostop do te deljene povezave z URL-jem po meri", "shared_link_edit_description_hint": "Vnesi opis skupne rabe", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dni", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", "shared_link_options": "Možnosti skupne povezave", + "shared_link_password_description": "Za dostop do te deljene povezave je potrebno geslo", "shared_links": "Povezave v skupni rabi", "shared_links_description": "Deli fotografije in videoposnetke s povezavo", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljenih fotografij & videoposnetkov.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "Naloži", + "upload_action_prompt": "{count} v čakalni vrsti za nalaganje", "upload_concurrency": "Sočasnost nalaganja", "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali želite varnostno kopirati izbrana sredstva na strežnik?", "upload_dialog_title": "Naloži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osvežite stran, da vidite nova sredstva za nalaganje.", + "upload_finished": "Nalaganje končano", "upload_progress": "Preostalo {remaining, number} - Obdelano {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočeno {count, plural, one {# podvojeno sredstvo} two {# podvojeni sredstvi} few {# podvojena sredstva} other {# podvojenih sredstev}}", "upload_status_duplicates": "Dvojniki", @@ -1954,6 +1976,7 @@ "upload_success": "Nalaganje je uspelo, osvežite stran, da vidite nova sredstva za nalaganje.", "upload_to_immich": "Naloži v Immich ({count})", "uploading": "Nalagam", + "uploading_media": "Nalaganje medijev", "url": "URL", "usage": "Uporaba", "use_biometric": "Uporabite biometrične podatke", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index 6216e671a8..f085adf532 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -479,6 +479,7 @@ "back_close_deselect": "Назад, затворите или опозовите избор", "background_location_permission": "Дозвола за локацију у позадини", "background_location_permission_content": "Да би се мењале мреже док се ради у позадини, Имих мора *увек* имати прецизан приступ локацији како би апликација могла да прочита име Wi-Fi мреже", + "backup": "Направи резервну копију", "backup_album_selection_page_albums_device": "Албума на уређају ({count})", "backup_album_selection_page_albums_tap": "Додирни да укључиш, додирни двапут да искључиш", "backup_album_selection_page_assets_scatter": "Записи се могу наћи у више различитих албума. Одатле албуми се могу укључити или искључити током процеса прављења позадинских копија.", @@ -1085,7 +1086,6 @@ "light": "Светло", "like_deleted": "Лајкуј избрисано", "link_motion_video": "Направи везу за видео запис", - "link_options": "Опције везе", "link_to_oauth": "Веза до OAuth-а", "linked_oauth_account": "Повезани OAuth налог", "list": "Излистај", @@ -1144,7 +1144,6 @@ "manage_your_devices": "Управљајте својим пријављеним уређајима", "manage_your_oauth_connection": "Управљајте својом OAuth везом", "map": "Мапа", - "map_assets_in_bound": "{count} фотографија", "map_assets_in_bounds": "{count} фотографија", "map_cannot_get_user_location": "Није могуц́е добити локацију корисника", "map_location_dialog_yes": "Да", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index a757146861..6eb88d7fd2 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -477,6 +477,7 @@ "back_close_deselect": "Nazad, zatvorite ili opozovite izbor", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Da bi se menjale mreže dok se radi u pozadini, Imih mora *uvek* imati precizan pristup lokaciji kako bi aplikacija mogla da pročita ime Wi-Fi mreže", + "backup": "Napravi rezervnu kopiju", "backup_album_selection_page_albums_device": "Albuma na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirni da uključiš, dodirni dvaput da isključiš", "backup_album_selection_page_assets_scatter": "Zapisi se mogu naći u više različitih albuma. Odatle albumi se mogu uključiti ili isključiti tokom procesa pravljenja pozadinskih kopija.", @@ -1070,7 +1071,6 @@ "light": "Svetlo", "like_deleted": "Lajkuj izbrisano", "link_motion_video": "Napravi vezu za video zapis", - "link_options": "Opcije veze", "link_to_oauth": "Veza do OAuth-a", "linked_oauth_account": "Povezani OAuth nalog", "list": "Izlistaj", @@ -1127,7 +1127,6 @@ "manage_your_devices": "Upravljajte svojim prijavljenim uređajima", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Mapa", - "map_assets_in_bound": "{count} fotografija", "map_assets_in_bounds": "{count} fotografija", "map_cannot_get_user_location": "Nije moguće dobiti lokaciju korisnika", "map_location_dialog_yes": "Da", diff --git a/i18n/sv.json b/i18n/sv.json index 00f5d95b66..e4842084f8 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -166,6 +166,10 @@ "metadata_settings_description": "Hantera metadata-inställningar", "migration_job": "Migrering", "migration_job_description": "Migrera miniatyrbilder för resurser och ansikten till den senaste mappstrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kör ansiktsigenkänning på nyligen upptäckta ansikten", + "nightly_tasks_database_cleanup_setting": "Uppgifter för databassanering", + "nightly_tasks_database_cleanup_setting_description": "Rensa bort gammal, utgången data från databasen", + "nightly_tasks_generate_memories_setting": "Generera minnen", "no_paths_added": "Inga vägar tillagda", "no_pattern_added": "Inga mönster tillagda", "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten på tidigare uppladdade tillgångar kör du", @@ -488,6 +492,7 @@ "back_close_deselect": "Tillbaka, stäng eller avmarkera", "background_location_permission": "Tillåtelse för bakgrundsplats", "background_location_permission_content": "För att kunna byta nätverk när appen körs i bakgrunden måste Immich *alltid* ha åtkomst till exakt plats så att appen kan läsa av Wi-Fi-nätverkets namn", + "backup": "Säkerhetskopiera", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera", "backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen.", @@ -1123,7 +1128,6 @@ "light": "Ljus", "like_deleted": "Gilla borttagen", "link_motion_video": "Länka rörlig video", - "link_options": "Alternativ för länk", "link_to_oauth": "Länk till OAuth", "linked_oauth_account": "Länkat OAuth konto", "list": "Lista", @@ -1185,7 +1189,6 @@ "manage_your_devices": "Hantera dina inloggade enheter", "manage_your_oauth_connection": "Hantera din OAuth-anslutning", "map": "Karta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foton", "map_cannot_get_user_location": "Kan inte hämta användarens plats", "map_location_dialog_yes": "Ja", diff --git a/i18n/ta.json b/i18n/ta.json index a91ea9d045..6e1ff10484 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -783,7 +783,6 @@ "light": "ஒளி", "like_deleted": "நீக்கப்பட்டதைப் போல", "link_motion_video": "இணைப்பு இயக்க வீடியோ", - "link_options": "இணைப்பு விருப்பங்கள்", "link_to_oauth": "OAUTH உடன் இணைப்பு", "linked_oauth_account": "இணைக்கப்பட்ட OAUTH கணக்கு", "list": "பட்டியல்", diff --git a/i18n/te.json b/i18n/te.json index 4a2e938c9a..0d5242891e 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -810,7 +810,6 @@ "light": "వెలుతురు", "like_deleted": "ఇస్టం తొలగించబడింది", "link_motion_video": "మోషన్ వీడియో లింక్ చేయండి", - "link_options": "లింక్ ఎంపికలు", "link_to_oauth": "OAuth కి లింక్ చేయండి", "linked_oauth_account": "లింక్ చేయబడిన OAuth ఖాతా", "list": "జాబితా", diff --git a/i18n/th.json b/i18n/th.json index 3cce9afd1c..8da7819ac5 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -489,6 +489,7 @@ "back_close_deselect": "ย้อนกลับ, ปิด, หรือยกเลิกการเลือก", "background_location_permission": "การอนุญาตระบุตำแหน่งพื้นหลัง", "background_location_permission_content": "เพื่อที่จะสลับการเชื่อมต่อขณะที่รันในพื้นหลัง Immich ต้องรู้ตำแหน่งที่แม่ยำตลอดเวลา เพื่อจะสามารถอ่านชื่อ Wi-Fi", + "backup": "สำรองข้อมูล", "backup_album_selection_page_albums_device": "อัลบั้มบนเครื่อง ({count})", "backup_album_selection_page_albums_tap": "กดเพื่อรวม กดสองครั้งเพื่อยกเว้น", "backup_album_selection_page_assets_scatter": "ทรัพยาการสามารถกระจายไปในหลายอัลบั้ม ดังนั้นอัลบั้มสามารถถูกรวมหรือยกเว้นในกระบวนการสำรองข้อมูล", @@ -1125,7 +1126,6 @@ "light": "สว่าง", "like_deleted": "ลบที่ถูกใจแล้ว", "link_motion_video": "ลิงก์วิดีโอเคลื่อนไหว", - "link_options": "ตัวเลือกลิงก์", "link_to_oauth": "ลิงก์ไปยัง OAuth", "linked_oauth_account": "ลิงก์บัญชีผู้ใช้ OAuth", "list": "รายการ", @@ -1188,7 +1188,6 @@ "manage_your_devices": "จัดการอุปกรณ์ของคุณ", "manage_your_oauth_connection": "จัดการการเชื่อมต่อ OAuth ของคุณ", "map": "แผนที่", - "map_assets_in_bound": "{count} รูปภาพ", "map_assets_in_bounds": "{count} รูปภาพ", "map_cannot_get_user_location": "ไม่สามารถหาตำแหน่งผู้ใช้งานได้", "map_location_dialog_yes": "ใช่", diff --git a/i18n/tr.json b/i18n/tr.json index b26f70acc2..c7a327472b 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -14,6 +14,7 @@ "add_a_location": "Lokasyon ekle", "add_a_name": "İsim ekle", "add_a_title": "Başlık ekle", + "add_birthday": "Doğum günü ekle", "add_endpoint": "Uç nokta ekle", "add_exclusion_pattern": "Hariç tutma deseni ekle", "add_import_path": "İçe aktarma yolu ekle", @@ -44,6 +45,13 @@ "backup_database": "Veritabanı yığını oluştur", "backup_database_enable_description": "Veritabanı yığınlarını etkinleştir", "backup_keep_last_amount": "Tutulması gereken geçmiş yığını miktarı", + "backup_onboarding_1_description": "bulutta veya başka bir fiziksel konumda bulunan yedek kopya.", + "backup_onboarding_2_description": "farklı cihazlarda yerel kopyalar. Bu, ana dosyaları ve bu dosyaların yerel yedeklerini içerir.", + "backup_onboarding_3_description": "Verilerinizin toplam kopyaları, orijinal dosyalar dahil. Bu, 1 adet dış mekan kopyası ve 2 adet yerel kopya içerir.", + "backup_onboarding_description": "Verilerinizi korumak için 3-2-1 yedekleme stratejisi önerilir. Kapsamlı bir yedekleme çözümü için, yüklediğiniz fotoğrafların/videoların yanı sıra Immich veritabanının kopyalarını da saklamalısınız.", + "backup_onboarding_footer": "Immich'i yedekleme hakkında daha fazla bilgi için lütfen belgelere bakın.", + "backup_onboarding_parts_title": "3-2-1 yedekleme şunları içerir:", + "backup_onboarding_title": "Yedeklemeler", "backup_settings": "Veritabanı yığını ayarları", "backup_settings_description": "Veritabanı döküm ayarlarını yönet.", "cleared_jobs": "{job} için işler temizlendi", @@ -166,6 +174,20 @@ "metadata_settings_description": "Metaveri ayarlarını yönet", "migration_job": "Birleştirme", "migration_job_description": "Varlıklar ve yüzler için resim çerçeve önizlemelerini en yeni klasör yapısına aktar", + "nightly_tasks_cluster_faces_setting_description": "Yeni algılanan yüzlerde yüz tanıma işlemini çalıştırın", + "nightly_tasks_cluster_new_faces_setting": "Yeni yüzleri bir araya getirin", + "nightly_tasks_database_cleanup_setting": "Veritabanı temizleme görevleri", + "nightly_tasks_database_cleanup_setting_description": "Veritabanından eski, süresi dolmuş verileri temizleyin", + "nightly_tasks_generate_memories_setting": "Anılar oluşturun", + "nightly_tasks_generate_memories_setting_description": "Varlıklardan yeni anılar yaratın", + "nightly_tasks_missing_thumbnails_setting": "Eksik küçük resimleri oluştur", + "nightly_tasks_missing_thumbnails_setting_description": "Küçük resim oluşturmak için küçük resim içermeyen varlıkları sıraya alın", + "nightly_tasks_settings": "Gece Görevleri Ayarları", + "nightly_tasks_settings_description": "Gece görevlerini yönet", + "nightly_tasks_start_time_setting": "Başlangıç saati", + "nightly_tasks_start_time_setting_description": "Sunucunun gece görevlerini çalıştırmaya başladığı saat", + "nightly_tasks_sync_quota_usage_setting": "Kota kullanımını senkronize et", + "nightly_tasks_sync_quota_usage_setting_description": "Mevcut kullanıma göre kullanıcı depolama kotasını güncelle", "no_paths_added": "Yol eklenmedi", "no_pattern_added": "Desen eklenmedi", "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobil yönlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanılacak Yönlendirme Adresi", "oauth_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayıcısı olduğunda etkinleştir, örneğin ''{callback}''", + "oauth_role_claim": "Rol Talebi", + "oauth_role_claim_description": "Bu iddianın varlığına göre otomatik olarak yönetici erişimi verin. İddia, 'kullanıcı' veya 'yönetici' olabilir.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth giriş ayarlarını yönet", "oauth_settings_more_details": "Bu özellik hakkında daha fazla bilgi için bu sayfayı ziyaret edin Dökümanlar.", @@ -357,10 +381,12 @@ "admin_password": "Yönetici Şifresi", "administration": "Yönetim", "advanced": "Gelişmiş", + "advanced_settings_beta_timeline_subtitle": "Yeni uygulama deneyimini deneyin", + "advanced_settings_beta_timeline_title": "Beta Zaman Çizelgesi", "advanced_settings_enable_alternate_media_filter_subtitle": "Eşleme sırasında medyayı alternatif ölçütlere göre süzgeçten geçirmek için bu seçeneği kullanın. Uygulamanın tüm albümleri algılamasında sorun yaşıyorsanız yalnızca bu durumda deneyin.", "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albüm eşleme süzgeci kullanın", "advanced_settings_log_level_title": "Günlük düzeyi: {level}", - "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar, cihazdaki öğelerin küçük resimlerini göstermekte çok yavaştır. Bunun yerine sunucudaki küçük resimleri göstermek için bu ayarı etkinleştirin.", + "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar yerel varlıklardan küçük resimleri yüklerken çok yavaş çalışır. Bu ayarı etkinleştirerek uzak görüntüleri yükleyin.", "advanced_settings_prefer_remote_title": "Uzak görüntüleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte göndermesi gereken proxy header'ları tanımlayın", "advanced_settings_proxy_headers_title": "Proxy Header'lar", @@ -379,6 +405,7 @@ "album_cover_updated": "Albüm Kapağı güncellendi", "album_delete_confirmation": "{album} albümünü silmek istediğinize emin misiniz?", "album_delete_confirmation_description": "Albüm paylaşılıyorsa, diğer kullanıcılar artık bu albüme erişemeyecektir.", + "album_deleted": "Albüm silindi", "album_info_card_backup_album_excluded": "HARİÇ", "album_info_card_backup_album_included": "DAHİL", "album_info_updated": "Albüm bilgisi güncellendi", @@ -388,6 +415,7 @@ "album_options": "Albüm seçenekleri", "album_remove_user": "Kullanıcıyı kaldır?", "album_remove_user_confirmation": "{user} kullanıcısını kaldırmak istediğinize emin misiniz?", + "album_search_not_found": "Aramanızla eşleşen albüm bulunamadı", "album_share_no_users": "Görünüşe göre bu albümü tüm kullanıcılarla paylaştınız veya paylaşacak herhangi bir başka kullanıcınız yok.", "album_updated": "Albüm güncellendi", "album_updated_setting_description": "Paylaşılan bir albüme yeni bir varlık eklendiğinde email bildirimi alın", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Varsayılan albüm sıralama düzeni", "albums_default_sort_order_description": "Yeni albüm oluştururken kullanılacak başlangıç varlık sıralama düzeni.", "albums_feature_description": "Diğer kullanıcılarla paylaşılabilen varlık koleksiyonları.", + "albums_on_device_count": "Cihazdaki albümler ({count})", "all": "Tümü", "all_albums": "Tüm Albümler", "all_people": "Tüm Kişiler", @@ -490,6 +519,7 @@ "back_close_deselect": "Geri, kapat veya seçimi kaldır", "background_location_permission": "Arka plan konum izni", "background_location_permission_content": "Arka planda çalışırken ağ değiştirmek için Immich'in *her zaman* tam konum erişimine sahip olması gerekir, böylece uygulama Wi-Fi ağının adını okuyabilir", + "backup": "Yedekle", "backup_album_selection_page_albums_device": "Cihazdaki albümler ({count})", "backup_album_selection_page_albums_tap": "Seçmek için dokunun, hariç tutmak için çift dokunun", "backup_album_selection_page_assets_scatter": "Varlıklar birden fazla albüme dağılabilir. Bu nedenle, yedekleme işlemi sırasında albümler dahil edilebilir veya hariç tutulabilir.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Yedekleme seçenekleri", "backup_setting_subtitle": "Arka planda ve ön planda yükleme ayarlarını düzenle", "backward": "Geriye doğru", + "beta_sync": "Beta Senkronizasyon Durumu", + "beta_sync_subtitle": "Yeni senkronizasyon sistemini yönetin", "biometric_auth_enabled": "Biyometrik kimlik doğrulama etkin", "biometric_locked_out": "Biyometrik kimlik doğrulaması kilitli", "biometric_no_options": "Biyometrik seçenek yok", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Önbelleği temizle", "cache_settings_clear_cache_button_title": "Uygulamanın önbelleğini temizleyin. Önbellek yeniden oluşturulana kadar uygulamanın performansını önemli ölçüde etkileyecektir.", "cache_settings_duplicated_assets_clear_button": "TEMİZLE", - "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından kara listeye alınan öğeler", + "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından yok sayılan fotoğraflar ve videolar", "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({count})", "cache_settings_statistics_album": "Kütüphane küçük resimleri", "cache_settings_statistics_full": "Tam çözünürlükte resimler", @@ -587,6 +619,7 @@ "cancel": "İptal", "cancel_search": "Aramayı iptal et", "canceled": "İptal edildi", + "canceling": "Vazgeçmek", "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alınamaz!", "cannot_update_the_description": "Açıklama güncellenemiyor", @@ -700,6 +733,7 @@ "current_server_address": "Mevcut sunucu adresi", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayıları dile ve bölgeye göre biçimlendirin", + "custom_url": "Özel URL", "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", @@ -719,7 +753,8 @@ "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", - "delete_action_prompt": "{count} kalıcı olarak silindi", + "delete_action_confirmation_message": "Bu varlığı silmek istediğinizden emin misiniz? Bu işlem, varlığı sunucunun çöp kutusuna taşıyacak ve yerel olarak silmek isteyip istemediğinizi soracaktır", + "delete_action_prompt": "{count} silindi", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu öğeler cihazınızdan ve Immich'ten kalıcı olarak silinecektir", @@ -737,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", + "delete_permanently": "Kalıcı olarak sil", + "delete_permanently_action_prompt": "{count} kalıcı olarak silindi", "delete_shared_link": "Paylaşılmış linki sil", "delete_shared_link_dialog_title": "Paylaşılan Bağlantı Sil", "delete_tag": "Etiketi sil", @@ -747,6 +784,7 @@ "description": "Açıklama", "description_input_hint_text": "Açıklama ekle...", "description_input_submit_error": "Açıklama güncellenirken hata oluştu, daha fazla ayrıntı için günlüğü kontrol edin", + "deselect_all": "Tümünü Seçimi Kaldır", "details": "Detaylar", "direction": "Yön", "disabled": "Devre dışı bırakıldı", @@ -764,6 +802,7 @@ "documentation": "Dokümantasyon", "done": "Bitti", "download": "İndir", + "download_action_prompt": "{count} varlık indiriliyor", "download_canceled": "İndirme iptal edildi", "download_complete": "İndirme tamamlandı", "download_enqueue": "İndirme sıraya alındı", @@ -790,6 +829,7 @@ "edit": "Düzenle", "edit_album": "Albümü düzenle", "edit_avatar": "Avatarı Düzenle", + "edit_birthday": "Doğum Günü Düzenle", "edit_date": "Tarihi Düzenle", "edit_date_and_time": "Tarih ve zamanı düzenleyin", "edit_description": "Açıklamayı düzenle", @@ -801,6 +841,7 @@ "edit_key": "Anahtarı düzenle", "edit_link": "Bağlantıyı düzenle", "edit_location": "Lokasyonu düzenleyin", + "edit_location_action_prompt": "{count} konum düzenlendi", "edit_location_dialog_title": "Konum", "edit_name": "İsmi düzenleyin", "edit_people": "Kişileri düzenle", @@ -819,6 +860,7 @@ "empty_trash": "Çöpü boşalt", "empty_trash_confirmation": "Çöp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çöp kutusundaki tüm varlıkları kalıcı olarak silecektir.\nBu işlemi geri alamazsınız!", "enable": "Etkinleştir", + "enable_backup": "Yedeklemeyi Etkinleştir", "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasını etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", @@ -955,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Açıklama Ekle...", + "exif_bottom_sheet_description_error": "Açıklama güncelleme hatası", "exif_bottom_sheet_details": "DETAYLAR", "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", @@ -975,6 +1018,8 @@ "explorer": "Geçmiş", "export": "Dışa Aktar", "export_as_json": "JSON olarak Dışa Aktar", + "export_database": "Veritabanını Dışa Aktar", + "export_database_description": "SQLite veritabanını dışa aktarın", "extension": "Uzantı", "external": "Harici", "external_libraries": "Harici kütüphaneler", @@ -1026,6 +1071,9 @@ "haptic_feedback_switch": "Dokunsal geri bildirimi aç", "haptic_feedback_title": "Dokunsal Geri Bildirim (Haptic Feedback)", "has_quota": "Kota var", + "hash_asset": "Hash varlığı", + "hashed_assets": "Hashlenmiş varlıklar", + "hashing": "Hashleme", "header_settings_add_header_tip": "Header Ekle", "header_settings_field_validator_msg": "Değer boş olamaz", "header_settings_header_name_input": "Header adı", @@ -1058,6 +1106,7 @@ "host": "Ana bilgisayar", "hour": "Saat", "id": "ID", + "idle": "Boşta", "ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say", "ignore_icloud_photos_description": "iCloud'a yüklenmiş fotoğraflar Immich sunucusuna yüklenmesin", "image": "Resim", @@ -1115,6 +1164,7 @@ "language_no_results_title": "Dil bulunamadı", "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", + "large_files": "Büyük Dosyalar", "last_seen": "Son görülme", "latest_version": "En son versiyon", "latitude": "Enlem", @@ -1134,13 +1184,14 @@ "light": "Açık", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", - "link_options": "Bağlantı seçenekleri", "link_to_oauth": "OAuth'a bağla", "linked_oauth_account": "Bağlı OAuth hesabı", "list": "Liste", "loading": "Yükleniyor", "loading_search_results_failed": "Arama sonuçları yüklenemedi", + "local": "Yerel", "local_asset_cast_failed": "Sunucuya yüklenmemiş bir varlık yansıtılamaz", + "local_assets": "Yerel Varlıklar", "local_network": "Yerel Wi-Fi", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", @@ -1197,7 +1248,6 @@ "manage_your_devices": "Cihazlarınızı yönetin", "manage_your_oauth_connection": "OAuth bağlantınızı yönetin", "map": "Harita", - "map_assets_in_bound": "{count} fotoğraf", "map_assets_in_bounds": "{count} fotoğraf", "map_cannot_get_user_location": "Kullanıcının konumu alınamıyor", "map_location_dialog_yes": "Evet", @@ -1250,6 +1300,7 @@ "more": "Daha fazla", "move": "Taşı", "move_off_locked_folder": "Kilitli klasörden taşı", + "move_to_lock_folder_action_prompt": "{count} kilitli klasöre eklendi", "move_to_locked_folder": "Kilitli klasöre taşı", "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tüm albümlerden kaldırılacak ve yalnızca kilitli klasörden görüntülenebilecektir", "moved_to_archive": "{count, plural, one {# öğe arşive taşındı} other {# öğe arşive taşındı}}", @@ -1296,6 +1347,7 @@ "no_results": "Sonuç bulunamadı", "no_results_description": "Eş anlamlı ya da daha genel anlamlı bir kelime deneyin", "no_shared_albums_message": "Fotoğrafları ve videoları ağınızdaki kişilerle paylaşmak için bir albüm oluşturun", + "no_uploads_in_progress": "Yükleme işlemi yok", "not_in_any_album": "Hiçbir albümde değil", "not_selected": "Seçilmedi", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha önce yüklenen varlıklar için bir depolama yolu etiketi uygulamak üzere şunu başlatın", @@ -1333,6 +1385,7 @@ "original": "orijinal", "other": "Diğer", "other_devices": "Diğer cihazlar", + "other_entities": "Diğer kuruluşlar", "other_variables": "Diğer değişkenler", "owned": "Sahip olunan", "owner": "Sahip", @@ -1464,6 +1517,7 @@ "purchase_server_description_2": "Destekçi statüsü", "purchase_server_title": "Sunucu", "purchase_settings_server_activated": "Sunucu ürün anahtarı, yönetici tarafından yönetilir", + "queue_status": "Sırada {count}/{total}", "rating": "Derecelendirme", "rating_clear": "Derecelendirmeyi temizle", "rating_count": "{count, plural, one {# yıldız} other {# yıldız}}", @@ -1492,6 +1546,8 @@ "refreshing_faces": "Yüzler yenileniyor", "refreshing_metadata": "Meta veriler yenileniyor", "regenerating_thumbnails": "Küçük resimler yeniden oluşturuluyor", + "remote": "Uzaktan", + "remote_assets": "Uzak Varlıklar", "remove": "Kaldır", "remove_assets_album_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} albümden çıkarmak istediğinizden emin misiniz?", "remove_assets_shared_link_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} bu paylaşılan bağlantıdan çıkarmak istediğinizden emin misiniz?", @@ -1529,19 +1585,25 @@ "reset_password": "Şifreyi sıfırla", "reset_people_visibility": "Kişilerin görünürlüğünü sıfırla", "reset_pin_code": "PIN kodunu sıfırlayın", + "reset_sqlite": "SQLite Veritabanını Sıfırla", + "reset_sqlite_confirmation": "SQLite veritabanını sıfırlamak istediğinizden emin misiniz? Verileri yeniden senkronize etmek için oturumu kapatıp tekrar oturum açmanız gerekecektir", + "reset_sqlite_success": "SQLite veritabanını başarıyla sıfırladınız", "reset_to_default": "Varsayılana sıfırla", "resolve_duplicates": "Çiftleri çöz", "resolved_all_duplicates": "Tüm çiftler çözüldü", "restore": "Geri yükle", "restore_all": "Tümünü geri yükle", + "restore_trash_action_prompt": "{count} çöp kutusundan geri yüklendi", "restore_user": "Kullanıcıyı geri yükle", "restored_asset": "Dosya geri yüklendi", "resume": "Devam et", "retry_upload": "Yeniden yüklemeyi dene", "review_duplicates": "Çiftleri gözden geçir", + "review_large_files": "Büyük dosyaları inceleyin", "role": "Rol", "role_editor": "Düzenleyici", "role_viewer": "Görüntüleyici", + "running": "Çalışıyor", "save": "Kaydet", "save_to_gallery": "Fotoğraflar'a kaydet", "saved_api_key": "API anahtarı kaydedildi", @@ -1673,6 +1735,7 @@ "settings_saved": "Ayarlar kaydedildi", "setup_pin_code": "PIN kodunu ayarlayın", "share": "Paylaş", + "share_action_prompt": "Paylaşılan {count} varlık", "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "Hazırlanıyor...", @@ -1694,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Panoya kopyalandı", "shared_link_clipboard_text": "Bağlantı: {link}\nParola: {password}", "shared_link_create_error": "Paylaşım bağlantısı oluşturulurken hata oluştu", + "shared_link_custom_url_description": "Özel bir URL ile bu paylaşılan bağlantıya erişin", "shared_link_edit_description_hint": "Açıklama yazın", "shared_link_edit_expire_after_option_day": "1 gün", "shared_link_edit_expire_after_option_days": "{count} gün", @@ -1719,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Paylaşılan Bağlantıları Yönet", "shared_link_options": "Paylaşılan bağlantı seçenekleri", + "shared_link_password_description": "Bu paylaşılan bağlantıya erişmek için şifre gerektirir", "shared_links": "Paylaşılan bağlantılar", "shared_links_description": "Fotoğraf ve videoları bir bağlantı ile paylaş", "shared_photos_and_videos_count": "{assetCount, plural, one {# paylaşılan fotoğraf veya video.} other {# paylaşılan fotoğraf & video.}}", @@ -1774,6 +1839,7 @@ "sort_title": "Başlık", "source": "Kaynak", "stack": "Yığın", + "stack_action_prompt": "{count} istiflenmiş", "stack_duplicates": "Çiftleri yığınla", "stack_select_one_photo": "Yığın için ana fotoğrafı seç", "stack_selected_photos": "Seçili fotoğrafları yığınla", @@ -1793,6 +1859,7 @@ "storage_quota": "Depolama Kotası", "storage_usage": "{used} / {available} kullanıldı", "submit": "Gönder", + "success": "Başarılı", "suggestions": "Öneriler", "sunrise_on_the_beach": "Plajda gün doğumu", "support": "Destek", @@ -1802,6 +1869,8 @@ "sync": "Senkronize et", "sync_albums": "Albümleri eşzamanla", "sync_albums_manual_subtitle": "Yüklenmiş fotoğraf ve videoları yedekleme için seçili albümler ile eşzamanlayın", + "sync_local": "Yerel Senkronizasyon", + "sync_remote": "Uzaktan Senkronizasyon", "sync_upload_album_setting_subtitle": "Seçili albümleri Immich'te oluşturun ve içindekileri Immich'e yükleyin.", "tag": "Etiket", "tag_assets": "Dosyaları etiketle", @@ -1812,6 +1881,7 @@ "tag_updated": "Etiket güncellendi: {tag}", "tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketlendi", "tags": "Etiketler", + "tap_to_run_job": "Başlatmak için dokunun", "template": "Şablon", "theme": "Tema", "theme_selection": "Tema seçimi", @@ -1844,6 +1914,7 @@ "total": "Toplam", "total_usage": "Toplam kullanım", "trash": "Çöp", + "trash_action_prompt": "{count} çöp kutusuna taşındı", "trash_all": "Hepsini sil", "trash_count": "Çöp kutusu {count, number}", "trash_delete_asset": "Ögeyi Sil/Çöpe gönder", @@ -1861,9 +1932,11 @@ "unable_to_change_pin_code": "PIN kodu değiştirilemedi", "unable_to_setup_pin_code": "PIN kodu ayarlanamadı", "unarchive": "Arşivden çıkar", + "unarchive_action_prompt": "{count} Arşivden kaldırıldı", "unarchived_count": "{count, plural, other {# arşivden çıkarıldı}}", "undo": "Geri al", "unfavorite": "Gözdelerden kaldır", + "unfavorite_action_prompt": "{count} Sık Kullanılanlar'dan kaldırıldı", "unhide_person": "Kişiyi göster", "unknown": "Bilinmeyen", "unknown_country": "Bilinmeyen ülke", @@ -1881,15 +1954,20 @@ "unselect_all_duplicates": "Tüm çiftlerin seçimini kaldır", "unselect_all_in": "{group} içindeki tüm seçimleri kaldır", "unstack": "Yığını kaldır", + "unstack_action_prompt": "{count} istiflenmemiş", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığını kaldırıldı", + "untagged": "Etiketlenmemiş", "up_next": "Sıradaki", "updated_at": "Güncellenme", "updated_password": "Şifreyi güncelle", "upload": "Yükle", + "upload_action_prompt": "{count} yükleme için sıraya alındı", "upload_concurrency": "Yükleme eşzamanlılığı", + "upload_details": "Yükleme Ayrıntıları", "upload_dialog_info": "Seçili öğeleri sunucuya yedeklemek istiyor musunuz?", "upload_dialog_title": "Öğe Yükle", "upload_errors": "{count, plural, one {# hata} other {# hatayla}} yükleme tamamlandı, yeni yüklenen dosyaları görmek için sayfayı güncelleyin.", + "upload_finished": "Yükleme tamamlandı", "upload_progress": "{remaining, number} kalan - {processed, number}/{total, number} işlendi", "upload_skipped_duplicates": "{count, plural, one {# çift dosya} other {# çift dosya}} atlandı", "upload_status_duplicates": "Çiftler", @@ -1898,6 +1976,7 @@ "upload_success": "Yükleme başarılı, yüklenen yeni ögeleri görebilmek için sayfayı yenileyin.", "upload_to_immich": "Immich'e Yükle ({count})", "uploading": "Yükleniyor", + "uploading_media": "Medya yükleme", "url": "URL", "usage": "Kullanım", "use_biometric": "Biyometri kullan", @@ -1918,6 +1997,7 @@ "user_usage_stats_description": "hesap kullanım istatistiklerini göster", "username": "Kullanıcı adı", "users": "Kullanıcılar", + "users_added_to_album_count": "Albüme {count, plural, one {# user} other {# users}} eklendi", "utilities": "Yardımcılar", "validate": "Doğrula", "validate_endpoint_error": "Lütfen geçerli bir URL girin", @@ -1936,6 +2016,7 @@ "view_album": "Albümü görüntüle", "view_all": "Tümünü gör", "view_all_users": "Tüm kullanıcıları görüntüle", + "view_details": "Ayrıntıları Görüntüle", "view_in_timeline": "Zaman çizelgesinde görüntüle", "view_link": "Bağlantıyı göster", "view_links": "Bağlantıları göster", diff --git a/i18n/uk.json b/i18n/uk.json index 8a375e627a..ca5e834991 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -10,14 +10,14 @@ "activity": "Активність", "activity_changed": "Активність {enabled, select, true {увімкнено} other {вимкнено}}", "add": "Додати", - "add_a_description": "Додади опис", + "add_a_description": "Додати опис", "add_a_location": "Додати місцезнаходження", "add_a_name": "Додати ім'я", "add_a_title": "Додати назву", "add_endpoint": "Додати кінцеву точку", - "add_exclusion_pattern": "Додайте шаблон виключення", + "add_exclusion_pattern": "Додати шаблон виключення", "add_import_path": "Додати шлях імпорту", - "add_location": "Додайте місцезнаходження", + "add_location": "Додати місцезнаходження", "add_more_users": "Додати користувачів", "add_partner": "Додати партнера", "add_path": "Додати шлях", @@ -489,6 +489,7 @@ "back_close_deselect": "Повернутися, закрити або скасувати вибір", "background_location_permission": "Дозвіл до місцезнаходження у фоні", "background_location_permission_content": "Щоб перемикати мережі у фоновому режимі, Immich має *завжди* мати доступ до точної геолокації, щоб зчитувати назву Wi-Fi мережі", + "backup": "Резервне копіювання", "backup_album_selection_page_albums_device": "Альбоми на пристрої ({count})", "backup_album_selection_page_albums_tap": "Торкніться, щоб включити, двічі, щоб виключити", "backup_album_selection_page_assets_scatter": "Елементи можуть належати до кількох альбомів водночас. Таким чином, альбоми можуть бути включені або вилучені під час резервного копіювання.", @@ -1128,7 +1129,6 @@ "light": "Світла", "like_deleted": "Лайк видалено", "link_motion_video": "Посилання на рухоме відео", - "link_options": "Налаштування посилання", "link_to_oauth": "Приєднання до OAuth", "linked_oauth_account": "Приєднаний акаунт OAuth", "list": "Перелік", @@ -1191,7 +1191,6 @@ "manage_your_devices": "Керуйте пристроями, які увійшли в систему", "manage_your_oauth_connection": "Налаштування підключеного OAuth", "map": "Мапа", - "map_assets_in_bound": "{count} фото", "map_assets_in_bounds": "{count} фото", "map_cannot_get_user_location": "Не можу отримати місцезнаходження", "map_location_dialog_yes": "Так", @@ -1566,8 +1565,8 @@ "search_filter_filename": "Пошук за назвою файлу", "search_filter_location": "Місцезнаходження", "search_filter_location_title": "Виберіть місцезнаходження", - "search_filter_media_type": "Тип носія", - "search_filter_media_type_title": "Виберіть тип носія", + "search_filter_media_type": "Тип медіа", + "search_filter_media_type_title": "Виберіть тип медіа", "search_filter_people_title": "Виберіть людей", "search_for": "Шукати для", "search_for_existing_person": "Пошук існуючої особи", diff --git a/i18n/vi.json b/i18n/vi.json index 10c883dc70..ad1738908b 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -485,6 +485,7 @@ "back_close_deselect": "Quay lại, đóng, hoặc bỏ chọn", "background_location_permission": "Quyền truy cập vị trí ở nền", "background_location_permission_content": "Để chuyển đổi mạng khi chạy ở chế độ nền, Immich *luôn* phải có quyền truy cập vị trí chính xác để ứng dụng có thể đọc tên mạng Wi-Fi", + "backup": "Sao lưu", "backup_album_selection_page_albums_device": "Album trên thiết bị ({count})", "backup_album_selection_page_albums_tap": "Nhấn để chọn, nhấn đúp để bỏ qua", "backup_album_selection_page_assets_scatter": "Ảnh có thể có trong nhiều album khác nhau. Trong quá trình sao lưu, bạn có thể chọn để sao lưu tất cả các album hoặc chỉ một số album nhất định.", @@ -1122,7 +1123,6 @@ "light": "Sáng", "like_deleted": "Đã xoá thích", "link_motion_video": "Liên kết video chuyển động", - "link_options": "Tùy chọn liên kết", "link_to_oauth": "Liên kết đến OAuth", "linked_oauth_account": "Tài khoản OAuth đã liên kết", "list": "Danh sách", @@ -1183,7 +1183,6 @@ "manage_your_devices": "Quản lý các thiết bị đã đăng nhập của bạn", "manage_your_oauth_connection": "Quản lý kết nối OAuth của bạn", "map": "Bản đồ", - "map_assets_in_bound": "{count} ảnh", "map_assets_in_bounds": "{count} ảnh", "map_cannot_get_user_location": "Không thể xác định vị trí của bạn", "map_location_dialog_yes": "Có", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index 334da7bfb1..b2acdc02eb 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -166,6 +166,19 @@ "metadata_settings_description": "管理詮釋資料設定", "migration_job": "遷移", "migration_job_description": "將項目和臉孔的縮圖移到新的延伸資料夾", + "nightly_tasks_cluster_faces_setting_description": "對新偵測到的臉孔進行辨識", + "nightly_tasks_database_cleanup_setting": "資料庫清理作業", + "nightly_tasks_database_cleanup_setting_description": "清除資料庫中舊的與已過期的資料", + "nightly_tasks_generate_memories_setting": "產生回憶", + "nightly_tasks_generate_memories_setting_description": "從項目建立新回憶", + "nightly_tasks_missing_thumbnails_setting": "產生缺少的縮圖", + "nightly_tasks_missing_thumbnails_setting_description": "將沒有縮圖的項目加入縮圖產生佇列", + "nightly_tasks_settings": "夜間任務設定", + "nightly_tasks_settings_description": "管理夜間任務", + "nightly_tasks_start_time_setting": "開始時間", + "nightly_tasks_start_time_setting_description": "伺服器開始執行夜間任務的時間", + "nightly_tasks_sync_quota_usage_setting": "同步配額使用量", + "nightly_tasks_sync_quota_usage_setting_description": "根據當前使用情況更新使用者儲存配額", "no_paths_added": "未添加路徑", "no_pattern_added": "未添加pattern", "note_apply_storage_label_previous_assets": "*註:執行套用儲存標籤前先上傳項目", @@ -357,6 +370,8 @@ "admin_password": "管理者密碼", "administration": "管理", "advanced": "進階", + "advanced_settings_beta_timeline_subtitle": "立即體驗新版應用程式", + "advanced_settings_beta_timeline_title": "測試版時間軸", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此選項可在同步時依照替代條件篩選媒體。僅當應用程式在偵測所有相簿時出現問題時才建議使用。", "advanced_settings_enable_alternate_media_filter_title": "[實驗]使用其他的裝置相簿同步篩選器", "advanced_settings_log_level_title": "日誌等級:{level}", @@ -379,6 +394,7 @@ "album_cover_updated": "已更新相簿封面", "album_delete_confirmation": "確定要刪除「{album}」(相簿)嗎?", "album_delete_confirmation_description": "如果已分享此相簿,其他使用者就無法再存取這本相簿了。", + "album_deleted": "相簿已刪除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已選中", "album_info_updated": "已更新相簿資訊", @@ -388,6 +404,7 @@ "album_options": "相簿選項", "album_remove_user": "移除使用者?", "album_remove_user_confirmation": "確定要移除 {user} 嗎?", + "album_search_not_found": "找不到符合搜尋條件的相簿", "album_share_no_users": "看來您與所有使用者共享了這本相簿,或沒有其他使用者可供分享。", "album_updated": "更新相簿時", "album_updated_setting_description": "當共享相簿有新項目時用電子郵件通知我", @@ -486,6 +503,7 @@ "back_close_deselect": "返回、關閉及取消選取", "background_location_permission": "背景存取位置權限", "background_location_permission_content": "開啟背景執行時自動切換網路,請充許 Immich 一律充許使用精確位置權限,以確認 Wi-Fi 網路名稱", + "backup": "備份", "backup_album_selection_page_albums_device": "裝置上的相簿({count})", "backup_album_selection_page_albums_tap": "點擊選取,連續點擊兩次取消", "backup_album_selection_page_assets_scatter": "項目會分散在不同相簿。因此,可以設定要備份的相簿。", @@ -549,6 +567,8 @@ "backup_options_page_title": "備份選項", "backup_setting_subtitle": "管理背景與前景上傳設定", "backward": "倒轉", + "beta_sync": "測試版同步狀態", + "beta_sync_subtitle": "管理新的同步系統", "biometric_auth_enabled": "生物辨識驗證已啟用", "biometric_locked_out": "您已被鎖定無法使用生物辨識驗證", "biometric_no_options": "無生物辨識選項可用", @@ -583,6 +603,7 @@ "cancel": "取消", "cancel_search": "取消搜尋", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "無法合併人物", "cannot_undo_this_action": "此步驟無法取消喔!", "cannot_update_the_description": "無法更新描述", @@ -698,6 +719,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", + "dark_theme": "切換深色主題", "date_after": "日期之後", "date_and_time": "日期與時間", "date_before": "日期之前", @@ -739,6 +761,7 @@ "description": "描述", "description_input_hint_text": "新增描述...", "description_input_submit_error": "更新描述時出錯,請檢查日誌以獲取更多詳細資訊", + "deselect_all": "取消全選", "details": "詳細資訊", "direction": "方向", "disabled": "停用", @@ -756,6 +779,7 @@ "documentation": "說明書", "done": "完成", "download": "下載", + "download_action_prompt": "正在下載 {count} 項目", "download_canceled": "下載已取消", "download_complete": "下載完成", "download_enqueue": "已加入下載隊列", @@ -811,6 +835,7 @@ "empty_trash": "清空垃圾桶", "empty_trash_confirmation": "確定要清空垃圾桶嗎?這會永久刪除 Immich 垃圾桶中所有的檔案。\n此步驟無法取消喔!", "enable": "啟用", + "enable_backup": "開啟備份", "enable_biometric_auth_description": "輸入您的 PIN 碼以啟用生物辨識驗證", "enabled": "己啟用", "end_date": "結束日期", @@ -967,6 +992,8 @@ "explorer": "總攬", "export": "匯出", "export_as_json": "匯出 JSON", + "export_database": "匯出資料庫", + "export_database_description": "匯出 SQLite 資料庫", "extension": "副檔名", "external": "外部", "external_libraries": "外部圖庫", @@ -978,6 +1005,7 @@ "failed_to_load_assets": "無法加載檔案", "failed_to_load_folder": "無法載入資料夾", "favorite": "收藏", + "favorite_action_prompt": "已新增 {count} 個到我的最愛", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏", "favorites_page_no_favorites": "未找到收藏項目", @@ -1016,6 +1044,8 @@ "haptic_feedback_switch": "啓用振動反饋", "haptic_feedback_title": "振動反饋", "has_quota": "配額", + "hash_asset": "雜湊項目", + "hashed_assets": "已雜湊項目", "header_settings_add_header_tip": "新增標頭", "header_settings_field_validator_msg": "設定不可為空", "header_settings_header_name_input": "標頭名稱", @@ -1114,12 +1144,13 @@ "light": "淺色", "like_deleted": "已刪除的收藏", "link_motion_video": "鏈結動態影片", - "link_options": "鏈結選項", "link_to_oauth": "連接 OAuth", "linked_oauth_account": "已連接 OAuth 帳號", "list": "列表", "loading": "載入中", "loading_search_results_failed": "載入搜尋結果失敗", + "local": "本地", + "local_assets": "本地項目", "local_network": "本地網路", "local_network_sheet_info": "當使用指定的 Wi-Fi 網路時,應用程式將透過此網址連線至伺服器", "location_permission": "位置權限", @@ -1175,7 +1206,6 @@ "manage_your_devices": "管理已登入的裝置", "manage_your_oauth_connection": "管理您的 OAuth 連接", "map": "地圖", - "map_assets_in_bound": "{count} 張照片", "map_assets_in_bounds": "{count} 張照片", "map_cannot_get_user_location": "無法獲取用戶位置", "map_location_dialog_yes": "確定", @@ -1274,6 +1304,7 @@ "no_results": "沒有結果", "no_results_description": "試試同義詞或更通用的關鍵字吧", "no_shared_albums_message": "建立相簿分享照片和影片", + "no_uploads_in_progress": "沒有正在上傳的項目", "not_in_any_album": "不在任何相簿中", "not_selected": "未選擇", "note_apply_storage_label_to_previously_uploaded assets": "*註:執行套用儲存標籤前先上傳項目", @@ -1465,6 +1496,8 @@ "refreshing_faces": "重整面部資料中", "refreshing_metadata": "正在重新整理詳細資料", "regenerating_thumbnails": "重新產生縮圖中", + "remote": "遠端", + "remote_assets": "遠端項目", "remove": "移除", "remove_assets_album_confirmation": "確定要從相簿中移除 {count, plural, other {# 個檔案}}嗎?", "remove_assets_shared_link_confirmation": "確定刪除共享連結中{count, plural, other {# 個項目}}嗎?", @@ -1500,6 +1533,8 @@ "reset_password": "重設密碼", "reset_people_visibility": "重設人物可見性", "reset_pin_code": "重置 PIN 碼", + "reset_sqlite": "重設 SQLite 資料庫", + "reset_sqlite_success": "已成功重設 SQLite 資料庫", "reset_to_default": "重設回預設", "resolve_duplicates": "解決重複項", "resolved_all_duplicates": "已解決所有重複項目", @@ -1864,13 +1899,14 @@ "upload_success": "上傳成功,要查看新上傳的檔案請重新整理頁面。", "upload_to_immich": "上傳至 Immich ({count})", "uploading": "上傳中", + "uploading_media": "媒體上傳中", "url": "網址", "usage": "用量", "use_biometric": "使用生物辨識", "use_current_connection": "使用目前的連線", "use_custom_date_range": "改用自訂日期範圍", "user": "使用者", - "user_has_been_deleted": "此用戶以被刪除", + "user_has_been_deleted": "此用戶已被刪除。", "user_id": "使用者 ID", "user_liked": "{user} 喜歡了 {type, select, photo {這張照片} video {這段影片} asset {這個檔案} other {它}}", "user_pin_code_settings": "PIN 碼", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index bcbdcc5d6b..10aef204bd 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -53,7 +53,7 @@ "confirm_email_below": "请输入“{email}”以进行确认", "confirm_reprocess_all_faces": "确定要对全部照片重新进行面部识别吗?这将同时清除所有已命名人物。", "confirm_user_password_reset": "确定要重置用户“{user}”的密码吗?", - "confirm_user_pin_code_reset": "确定要重置{user}的PIN码吗?", + "confirm_user_pin_code_reset": "确定要重置用户“{user}”的PIN码吗?", "create_job": "创建任务", "cron_expression": "Cron 表达式", "cron_expression_description": "使用 Cron 格式设置扫描间隔。更多详细信息请参阅 Crontab Guru", @@ -389,7 +389,7 @@ "advanced_settings_tile_subtitle": "高级用户设置", "advanced_settings_troubleshooting_subtitle": "启用用于故障排除的额外功能", "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, one {#月龄} other {#月龄}}", + "age_months": "{months, plural, one {#个月} other {#个月}}", "age_year_months": "1岁{months, plural, one {#个月} other {#个月}}", "age_years": "{years, plural, other {#岁}}", "album_added": "被添加到相册", @@ -397,6 +397,7 @@ "album_cover_updated": "相册封面已更新", "album_delete_confirmation": "确定要删除相册“{album}”吗?", "album_delete_confirmation_description": "如果该相册是共享的,其他用户将无法再访问它。", + "album_deleted": "相册已删除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已选中", "album_info_updated": "相册信息已更新", @@ -419,7 +420,7 @@ "album_viewer_appbar_share_err_title": "修改相册标题失败", "album_viewer_appbar_share_leave": "退出共享", "album_viewer_appbar_share_to": "共享给", - "album_viewer_page_share_add_users": "创建用户", + "album_viewer_page_share_add_users": "邀请他人", "album_with_link_access": "拥有此链接的任何人均可查看本相册中的照片和人物。", "albums": "相册", "albums_count": "{count, plural, one {{count, number} 个相册} other {{count, number} 个相册}}", @@ -510,6 +511,7 @@ "back_close_deselect": "返回、关闭或反选", "background_location_permission": "后台定位权限", "background_location_permission_content": "为了在后台运行时切换网络,Immich 必须*始终*拥有精确的位置访问权限,这样应用程序才能读取 Wi-Fi 网络的名称", + "backup": "备份", "backup_album_selection_page_albums_device": "设备上的相册({count})", "backup_album_selection_page_albums_tap": "单击选中,双击取消", "backup_album_selection_page_assets_scatter": "项目会分散在多个相册中。因此,可以在备份过程中包含或排除相册。", @@ -723,6 +725,7 @@ "current_server_address": "当前服务器地址", "custom_locale": "自定义地区", "custom_locale_description": "日期和数字显示格式跟随语言和地区", + "custom_url": "自定义URL", "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", @@ -742,9 +745,10 @@ "default_locale": "默认地区", "default_locale_description": "根据您的浏览器地区设置日期和数字显示格式", "delete": "删除", - "delete_action_prompt": "已永久删除 {count} 项", + "delete_action_confirmation_message": "您确定要删除此资产吗?此操作会将资产移至服务器回收站,并会提示您是否要在本地删除它", + "delete_action_prompt": "已删除 {count} 项", "delete_album": "删除相册", - "delete_api_key_prompt": "确定删除此 API 密钥吗?", + "delete_api_key_prompt": "是否确认删除此 API 密钥?", "delete_dialog_alert": "这些项目将从 Immich 和您的设备中永久删除", "delete_dialog_alert_local": "这些项目将从您的移动设备中永久删除,但仍然可以从 Immich 服务器中再次获取", "delete_dialog_alert_local_non_backed_up": "部分项目还未备份至 Immich 服务器,将从您的移动设备中永久删除", @@ -760,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "仅删除已备份项目", "delete_local_dialog_ok_force": "确认删除", "delete_others": "删除其它", + "delete_permanently": "永久删除", + "delete_permanently_action_prompt": "已永久删除 {count} 项", "delete_shared_link": "删除共享链接", "delete_shared_link_dialog_title": "删除共享链接", "delete_tag": "删除标签", @@ -1002,6 +1008,8 @@ "explorer": "资源管理器", "export": "导出", "export_as_json": "导出为 JSON", + "export_database": "导出数据库", + "export_database_description": "导出 SQLite 数据库", "extension": "扩展", "external": "外部的", "external_libraries": "外部图库", @@ -1034,7 +1042,7 @@ "folders": "文件夹", "folders_feature_description": "在文件夹视图中浏览文件系统上的照片和视频", "forward": "向前", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google Cast 投屏", "gcast_enabled_description": "该功能需要加载来自 Google 的外部资源。", "general": "通用", "get_help": "获取帮助", @@ -1146,6 +1154,7 @@ "language_no_results_title": "未找到对应语言", "language_search_hint": "搜索语言...", "language_setting_description": "选择您的语言偏好", + "large_files": "大文件", "last_seen": "最后上线于", "latest_version": "最新版本", "latitude": "纬度", @@ -1165,7 +1174,6 @@ "light": "浅色", "like_deleted": "已删除的收藏", "link_motion_video": "链接动态视频", - "link_options": "链接选项", "link_to_oauth": "绑定 OAuth", "linked_oauth_account": "已绑定 OAuth 账户", "list": "列表", @@ -1230,8 +1238,7 @@ "manage_your_devices": "管理已登录设备", "manage_your_oauth_connection": "管理您的 OAuth 绑定", "map": "地图", - "map_assets_in_bound": "{count} 张照片", - "map_assets_in_bounds": "{count} 张照片", + "map_assets_in_bounds": "{count, plural, one {# 张照片} other {# 张照片}}", "map_cannot_get_user_location": "无法获取用户位置", "map_location_dialog_yes": "是", "map_location_picker_page_use_location": "使用此位置", @@ -1582,6 +1589,7 @@ "resume": "继续", "retry_upload": "重新上传", "review_duplicates": "检查重复项", + "review_large_files": "查看大文件", "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", @@ -1739,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "复制到剪贴板", "shared_link_clipboard_text": "链接:{link}\n密码:{password}", "shared_link_create_error": "创建共享链接出错", + "shared_link_custom_url_description": "使用自定义URL访问此共享链接", "shared_link_edit_description_hint": "编辑共享描述", "shared_link_edit_expire_after_option_day": "1天", "shared_link_edit_expire_after_option_days": "{count} 天", @@ -1764,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "管理共享链接", "shared_link_options": "共享链接选项", + "shared_link_password_description": "需要密码才能访问此共享链接", "shared_links": "共享链接", "shared_links_description": "通过链接分享照片和视频", "shared_photos_and_videos_count": "{assetCount, plural, other {#项已共享照片&视频。}}", @@ -1941,11 +1951,13 @@ "updated_at": "已更新", "updated_password": "更新密码", "upload": "上传", + "upload_action_prompt": "有{count}个待上传", "upload_concurrency": "上传并发", "upload_details": "上传详情", "upload_dialog_info": "是否要将所选项目备份到服务器?", "upload_dialog_title": "上传项目", "upload_errors": "上传完成,出现{count, plural, one {#个错误} other {#个错误}},刷新页面以查看新上传的项目。", + "upload_finished": "上传完成", "upload_progress": "剩余{remaining, number} - 已处理 {processed, number}/{total, number}", "upload_skipped_duplicates": "已跳过{count, plural, one {#个重复项} other {#个重复项}}", "upload_status_duplicates": "重复项", @@ -1954,6 +1966,7 @@ "upload_success": "上传成功,刷新页面查看新上传的项目。", "upload_to_immich": "上传至 Immich({count})", "uploading": "正在上传", + "uploading_media": "文件上传中", "url": "URL", "usage": "用量", "use_biometric": "使用生物识别", From f85d8add012ed40cc965f44a2b22872394dad698 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:18:18 +0530 Subject: [PATCH 149/169] fix: json encoding failure during live photo download (#20444) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/repositories/download.repository.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 6f38a802e2..c4b31e9d93 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -21,7 +21,7 @@ class DownloadRepository { group: '', updates: Updates.statusAndProgress, ); - static final _dummyMetadata = {'part': LivePhotosPart.image, 'id': ''}; + static final _dummyMetadata = {'part': LivePhotosPart.image.index, 'id': ''}; void Function(TaskStatusUpdate)? onImageDownloadStatus; @@ -102,7 +102,7 @@ class DownloadRepository { continue; } - _dummyMetadata['part'] = LivePhotosPart.image; + _dummyMetadata['part'] = LivePhotosPart.image.index; _dummyMetadata['id'] = id; tasks[taskIndex++] = DownloadTask( taskId: id, @@ -114,7 +114,7 @@ class DownloadRepository { metaData: json.encode(_dummyMetadata), ); - _dummyMetadata['part'] = LivePhotosPart.video; + _dummyMetadata['part'] = LivePhotosPart.video.index; tasks[taskIndex++] = DownloadTask( taskId: livePhotoVideoId, url: url, From 641a3baaddada4fd90e11f8a15eea94641d32d4b Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:59:00 +0530 Subject: [PATCH 150/169] fix(mobile): add partial index based on library ID to remote assets (#20214) * feat: add libraryId to SyncAssetV1 * add partial index # Conflicts: # mobile/drift_schemas/main/drift_schema_v5.json # mobile/lib/infrastructure/repositories/db.repository.dart # mobile/lib/infrastructure/repositories/db.repository.steps.dart # mobile/test/drift/main/generated/schema_v5.dart * chore: make build * rebase --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../drift_schemas/main/drift_schema_v6.json | 1 + .../entities/remote_asset.entity.dart | 14 +- .../entities/remote_asset.entity.drift.dart | 85 +- .../repositories/db.repository.dart | 11 +- .../repositories/db.repository.drift.dart | 4 +- .../repositories/db.repository.steps.dart | 394 + .../repositories/sync_stream.repository.dart | 1 + mobile/openapi/lib/model/sync_asset_v1.dart | 14 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v6.dart | 6448 +++++++++++++++++ open-api/immich-openapi-specs.json | 5 + server/src/database.ts | 1 + server/src/dtos/sync.dto.ts | 1 + server/src/queries/sync.repository.sql | 5 + server/src/services/job.service.ts | 1 + .../specs/sync/sync-album-asset.spec.ts | 2 + .../test/medium/specs/sync/sync-asset.spec.ts | 2 + .../specs/sync/sync-partner-asset.spec.ts | 2 + 18 files changed, 6985 insertions(+), 11 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v6.json create mode 100644 mobile/test/drift/main/generated/schema_v6.dart diff --git a/mobile/drift_schemas/main/drift_schema_v6.json b/mobile/drift_schemas/main/drift_schema_v6.json new file mode 100644 index 0000000000..adb2484006 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v6.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 7ee2e76ff6..ecc0aa3d76 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,7 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true) +@TableIndex(name: 'idx_remote_asset_owner_checksum', columns: {#ownerId, #checksum}) +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum +ON remote_asset_entity (owner_id, checksum) +WHERE (library_id IS NULL); +''') +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum +ON remote_asset_entity (owner_id, library_id, checksum) +WHERE (library_id IS NOT NULL); +''') @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); @@ -30,6 +40,8 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin TextColumn get stackId => text().nullable()(); + TextColumn get libraryId => text().nullable()(); + @override Set get primaryKey => {id}; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 6bd416b259..9681d1e75d 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -30,6 +30,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder = i0.Value livePhotoVideoId, required i2.AssetVisibility visibility, i0.Value stackId, + i0.Value libraryId, }); typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = i1.RemoteAssetEntityCompanion Function({ @@ -50,6 +51,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = i0.Value livePhotoVideoId, i0.Value visibility, i0.Value stackId, + i0.Value libraryId, }); final class $$RemoteAssetEntityTableReferences @@ -189,6 +191,11 @@ class $$RemoteAssetEntityTableFilterComposer builder: (column) => i0.ColumnFilters(column), ); + i0.ColumnFilters get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnFilters(column), + ); + i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( composer: this, @@ -306,6 +313,11 @@ class $$RemoteAssetEntityTableOrderingComposer builder: (column) => i0.ColumnOrderings(column), ); + i0.ColumnOrderings get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnOrderings(column), + ); + i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( composer: this, @@ -402,6 +414,9 @@ class $$RemoteAssetEntityTableAnnotationComposer i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); + i0.GeneratedColumn get libraryId => + $composableBuilder(column: $table.libraryId, builder: (column) => column); + i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -481,6 +496,7 @@ class $$RemoteAssetEntityTableTableManager i0.Value visibility = const i0.Value.absent(), i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityCompanion( name: name, type: type, @@ -499,6 +515,7 @@ class $$RemoteAssetEntityTableTableManager livePhotoVideoId: livePhotoVideoId, visibility: visibility, stackId: stackId, + libraryId: libraryId, ), createCompanionCallback: ({ @@ -519,6 +536,7 @@ class $$RemoteAssetEntityTableTableManager i0.Value livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityCompanion.insert( name: name, type: type, @@ -537,6 +555,7 @@ class $$RemoteAssetEntityTableTableManager livePhotoVideoId: livePhotoVideoId, visibility: visibility, stackId: stackId, + libraryId: libraryId, ), withReferenceMapper: (p0) => p0 .map( @@ -607,9 +626,9 @@ typedef $$RemoteAssetEntityTableProcessedTableManager = i1.RemoteAssetEntityData, i0.PrefetchHooks Function({bool ownerId}) >; -i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', +i0.Index get idxRemoteAssetOwnerChecksum => i0.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @@ -814,6 +833,17 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity type: i0.DriftSqlType.string, requiredDuringInsert: false, ); + static const i0.VerificationMeta _libraryIdMeta = const i0.VerificationMeta( + 'libraryId', + ); + @override + late final i0.GeneratedColumn libraryId = i0.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ name, @@ -833,6 +863,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity livePhotoVideoId, visibility, stackId, + libraryId, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -950,6 +981,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), ); } + if (data.containsKey('library_id')) { + context.handle( + _libraryIdMeta, + libraryId.isAcceptableOrUnknown(data['library_id']!, _libraryIdMeta), + ); + } return context; } @@ -1034,6 +1071,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity i0.DriftSqlType.string, data['${effectivePrefix}stack_id'], ), + libraryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), ); } @@ -1073,6 +1114,7 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; + final String? libraryId; const RemoteAssetEntityData({ required this.name, required this.type, @@ -1091,6 +1133,7 @@ class RemoteAssetEntityData extends i0.DataClass this.livePhotoVideoId, required this.visibility, this.stackId, + this.libraryId, }); @override Map toColumns(bool nullToAbsent) { @@ -1136,6 +1179,9 @@ class RemoteAssetEntityData extends i0.DataClass if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = i0.Variable(libraryId); + } return map; } @@ -1166,6 +1212,7 @@ class RemoteAssetEntityData extends i0.DataClass serializer.fromJson(json['visibility']), ), stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), ); } @override @@ -1193,6 +1240,7 @@ class RemoteAssetEntityData extends i0.DataClass i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), ), 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), }; } @@ -1214,6 +1262,7 @@ class RemoteAssetEntityData extends i0.DataClass i0.Value livePhotoVideoId = const i0.Value.absent(), i2.AssetVisibility? visibility, i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityData( name: name ?? this.name, type: type ?? this.type, @@ -1238,6 +1287,7 @@ class RemoteAssetEntityData extends i0.DataClass : this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( @@ -1268,6 +1318,7 @@ class RemoteAssetEntityData extends i0.DataClass ? data.visibility.value : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, ); } @@ -1290,7 +1341,8 @@ class RemoteAssetEntityData extends i0.DataClass ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } @@ -1314,6 +1366,7 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId, visibility, stackId, + libraryId, ); @override bool operator ==(Object other) => @@ -1335,7 +1388,8 @@ class RemoteAssetEntityData extends i0.DataClass other.deletedAt == this.deletedAt && other.livePhotoVideoId == this.livePhotoVideoId && other.visibility == this.visibility && - other.stackId == this.stackId); + other.stackId == this.stackId && + other.libraryId == this.libraryId); } class RemoteAssetEntityCompanion @@ -1357,6 +1411,7 @@ class RemoteAssetEntityCompanion final i0.Value livePhotoVideoId; final i0.Value visibility; final i0.Value stackId; + final i0.Value libraryId; const RemoteAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -1375,6 +1430,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), this.visibility = const i0.Value.absent(), this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }); RemoteAssetEntityCompanion.insert({ required String name, @@ -1394,6 +1450,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }) : name = i0.Value(name), type = i0.Value(type), id = i0.Value(id), @@ -1418,6 +1475,7 @@ class RemoteAssetEntityCompanion i0.Expression? livePhotoVideoId, i0.Expression? visibility, i0.Expression? stackId, + i0.Expression? libraryId, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -1437,6 +1495,7 @@ class RemoteAssetEntityCompanion if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, if (visibility != null) 'visibility': visibility, if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, }); } @@ -1458,6 +1517,7 @@ class RemoteAssetEntityCompanion i0.Value? livePhotoVideoId, i0.Value? visibility, i0.Value? stackId, + i0.Value? libraryId, }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, @@ -1477,6 +1537,7 @@ class RemoteAssetEntityCompanion livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, ); } @@ -1538,6 +1599,9 @@ class RemoteAssetEntityCompanion if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); } + if (libraryId.present) { + map['library_id'] = i0.Variable(libraryId.value); + } return map; } @@ -1560,12 +1624,21 @@ class RemoteAssetEntityCompanion ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } } +i0.Index get uQRemoteAssetsOwnerChecksum => i0.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', +); +i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', +); i0.Index get idxRemoteAssetChecksum => i0.Index( 'idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 353cabf31e..0458a5b254 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 5; + int get schemaVersion => 6; @override MigrationStrategy get migration => MigrationStrategy( @@ -103,6 +103,15 @@ class Drift extends $Drift implements IDatabaseRepository { ), ); }, + from5To6: (m, v6) async { + // Drops the (checksum, ownerId) and adds it back as (ownerId, checksum) + await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum'); + await m.create(v6.idxRemoteAssetOwnerChecksum); + // Adds libraryId to remote_asset_entity + await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId); + await m.create(v6.uQRemoteAssetsOwnerChecksum); + await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 296b87900e..2b7203eb46 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -84,7 +84,9 @@ abstract class $Drift extends i0.GeneratedDatabase { localAlbumEntity, localAlbumAssetEntity, i4.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, + i2.idxRemoteAssetOwnerChecksum, + i2.uQRemoteAssetsOwnerChecksum, + i2.uQRemoteAssetsOwnerLibraryChecksum, i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 8129bba00c..32b309881e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -2319,11 +2319,398 @@ i1.GeneratedColumn _column_85(String aliasedName) => type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), ); + +final class Schema6 extends i0.VersionedSchema { + Schema6({required super.database}) : super(version: 6); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape17 extends i0.VersionedTable { + Shape17({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get livePhotoVideoId => + columnsByName['live_photo_video_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get libraryId => + columnsByName['library_id']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_86(String aliasedName) => + i1.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -2347,6 +2734,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from4To5(migrator, schema); return 5; + case 5: + final schema = Schema6(database: database); + final migrator = i1.Migrator(database, schema); + await from5To6(migrator, schema); + return 6; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -2358,11 +2750,13 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, from3To4: from3To4, from4To5: from4To5, + from5To6: from5To6, ), ); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 2eefa298f8..52ffaabca9 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -121,6 +121,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { visibility: Value(asset.visibility.toAssetVisibility()), livePhotoVideoId: Value(asset.livePhotoVideoId), stackId: Value(asset.stackId), + libraryId: Value(asset.libraryId), ); batch.insert( diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 4c42d08a5f..f0d5097ea4 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -20,6 +20,7 @@ class SyncAssetV1 { required this.fileModifiedAt, required this.id, required this.isFavorite, + required this.libraryId, required this.livePhotoVideoId, required this.localDateTime, required this.originalFileName, @@ -44,6 +45,8 @@ class SyncAssetV1 { bool isFavorite; + String? libraryId; + String? livePhotoVideoId; DateTime? localDateTime; @@ -69,6 +72,7 @@ class SyncAssetV1 { other.fileModifiedAt == fileModifiedAt && other.id == id && other.isFavorite == isFavorite && + other.libraryId == libraryId && other.livePhotoVideoId == livePhotoVideoId && other.localDateTime == localDateTime && other.originalFileName == originalFileName && @@ -88,6 +92,7 @@ class SyncAssetV1 { (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + (isFavorite.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (localDateTime == null ? 0 : localDateTime!.hashCode) + (originalFileName.hashCode) + @@ -98,7 +103,7 @@ class SyncAssetV1 { (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -125,6 +130,11 @@ class SyncAssetV1 { } json[r'id'] = this.id; json[r'isFavorite'] = this.isFavorite; + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } if (this.livePhotoVideoId != null) { json[r'livePhotoVideoId'] = this.livePhotoVideoId; } else { @@ -168,6 +178,7 @@ class SyncAssetV1 { fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, isFavorite: mapValueOfType(json, r'isFavorite')!, + libraryId: mapValueOfType(json, r'libraryId'), livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'), localDateTime: mapDateTime(json, r'localDateTime', r''), originalFileName: mapValueOfType(json, r'originalFileName')!, @@ -230,6 +241,7 @@ class SyncAssetV1 { 'fileModifiedAt', 'id', 'isFavorite', + 'libraryId', 'livePhotoVideoId', 'localDateTime', 'originalFileName', diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index c42542afb3..d59002bf56 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -8,6 +8,7 @@ import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; import 'schema_v5.dart' as v5; +import 'schema_v6.dart' as v6; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -23,10 +24,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v4.DatabaseAtV4(db); case 5: return v5.DatabaseAtV5(db); + case 6: + return v6.DatabaseAtV6(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4, 5]; + static const versions = const [1, 2, 3, 4, 5, 6]; } diff --git a/mobile/test/drift/main/generated/schema_v6.dart b/mobile/test/drift/main/generated/schema_v6.dart new file mode 100644 index 0000000000..97b91caa07 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v6.dart @@ -0,0 +1,6448 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV6 extends GeneratedDatabase { + DatabaseAtV6(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 6; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 9a1e6a6937..9dc7540dd8 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -14786,6 +14786,10 @@ "isFavorite": { "type": "boolean" }, + "libraryId": { + "nullable": true, + "type": "string" + }, "livePhotoVideoId": { "nullable": true, "type": "string" @@ -14832,6 +14836,7 @@ "fileModifiedAt", "id", "isFavorite", + "libraryId", "livePhotoVideoId", "localDateTime", "originalFileName", diff --git a/server/src/database.ts b/server/src/database.ts index 052955636b..e7946cd8fb 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -354,6 +354,7 @@ export const columns = { 'asset.duration', 'asset.livePhotoVideoId', 'asset.stackId', + 'asset.libraryId', ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 66061e7bbe..9c304c0d3c 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -115,6 +115,7 @@ export class SyncAssetV1 { visibility!: AssetVisibility; livePhotoVideoId!: string | null; stackId!: string | null; + libraryId!: string | null; } @ExtraModel() diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 5c80460158..28c6f32acc 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -66,6 +66,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -95,6 +96,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -357,6 +359,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -605,6 +608,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -652,6 +656,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index c67f3af39f..0116c869c6 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -382,6 +382,7 @@ export class JobService extends BaseService { visibility: asset.visibility, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, exif: { assetId: exif.assetId, diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 9a42c0f027..3002b99071 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -38,6 +38,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: '0:10:00.00000', livePhotoVideoId: null, stackId: null, + libraryId: null, }); const { album } = await ctx.newAlbum({ ownerId: user2.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); @@ -64,6 +65,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: asset.duration, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, type: SyncEntityType.AlbumAssetV1, }, diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 52d6bcb524..ce83eed98c 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -36,6 +36,7 @@ describe(SyncEntityType.AssetV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); @@ -59,6 +60,7 @@ describe(SyncEntityType.AssetV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: 'AssetV1', }, diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 2daa750bf3..e9dc7403bd 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -40,6 +40,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); @@ -65,6 +66,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: SyncEntityType.PartnerAssetV1, }, From d73335ecbcd21ed611381a4ed83b6b670006a20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 30 Jul 2025 21:13:19 +0200 Subject: [PATCH 151/169] docs: add config example for Authelia (#20223) --- docs/docs/administration/oauth.md | 89 +++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 833b70f77a..7450ae1b08 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -64,7 +64,7 @@ Once you have a new OAuth client application configured, Immich can be configure | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** | -| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | +| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (empty for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | @@ -106,6 +106,89 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to ## Example Configuration +
+Authelia Example + +### Authelia Example + +Here's an example of OAuth configured for Authelia: + +This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich. +The configuration concerning the quota is optional. + +```yaml +authentication_backend: + ldap: + # The LDAP server configuration goes here. + # See: https://www.authelia.com/c/ldap + attributes: + extra: + immichquota: # The attribute name from LDAP + name: 'immich_quota' + multi_valued: false + value_type: 'integer' +identity_providers: + oidc: + ## The other portions of the mandatory OpenID Connect 1.0 configuration go here. + ## See: https://www.authelia.com/c/oidc + claims_policies: + immich_policy: + custom_claims: + immich_quota: + attribute: 'immich_quota' + scopes: + immich_scope: + claims: + - 'immich_quota' + + clients: + - client_id: 'immich' + client_name: 'Immich' + # https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret + client_secret: $pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' + public: false + require_pkce: false + redirect_uris: + - 'https://example.immich.app/auth/login' + - 'https://example.immich.app/user-settings' + - 'app.immich:///oauth-callback' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'immich_scope' + claims_policy: 'immich_policy' + response_types: + - 'code' + grant_types: + - 'authorization_code' + id_token_signed_response_alg: 'RS256' + userinfo_signed_response_alg: 'RS256' + token_endpoint_auth_method: 'client_secret_post' +``` + +Configuration of OAuth in Immich System Settings + +| Setting | Value | +| ---------------------------------- | ------------------------------------------------------------------- | +| Issuer URL | `https://example.immich.app/.well-known/openid-configuration` | +| Client ID | immich | +| Client Secret | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... | +| Token Endpoint Auth Method | client_secret_post | +| Scope | openid email profile immich_scope | +| ID Token Signed Response Algorithm | RS256 | +| Userinfo Signed Response Algorithm | RS256 | +| Storage Label Claim | uid | +| Storage Quota Claim | immich_quota | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | +| Button Text | Sign in with Authelia (optional) | +| Auto Register | Enabled (optional) | +| Auto Launch | Enabled (optional) | +| Mobile Redirect URI Override | Disable | +| Mobile Redirect URI | | + +
+
Authentik Example @@ -128,7 +211,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Authentik (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled (optional) | @@ -159,7 +242,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Google (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled | From f416342effe89a3b69ea937e8201462e8b7e721d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 31 Jul 2025 01:02:38 +0530 Subject: [PATCH 152/169] fix: clear local file cache before upload (#20448) * clear local file cache before upload * clear cache during hashing * fix test * add button to clear cache manually * add button to clear cache manually --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- i18n/en.json | 1 + mobile/lib/domain/services/hash.service.dart | 1 + .../repositories/storage.repository.dart | 10 ++++++++++ mobile/lib/services/upload.service.dart | 4 ++++ .../beta_sync_settings/beta_sync_settings.dart | 13 +++++++++++++ mobile/test/domain/services/hash_service_test.dart | 1 + 6 files changed, 30 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index 94f6920745..c4cf101b73 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -653,6 +653,7 @@ "clear": "Clear", "clear_all": "Clear all", "clear_all_recent_searches": "Clear all recent searches", + "clear_file_cache": "Clear File Cache", "clear_message": "Clear message", "clear_value": "Clear value", "client_cert_dialog_msg_confirm": "OK", diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 2eb9aec4db..2a07320906 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -104,6 +104,7 @@ class HashService { DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); + await _storageRepository.clearCache(); } } diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 0cf4f20ba8..18302aeb7d 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -66,4 +66,14 @@ class StorageRepository { } return entity; } + + Future clearCache() async { + final log = Logger('StorageRepository'); + + try { + await PhotoManager.clearFileCache(); + } catch (error, stackTrace) { + log.warning("Error clearing cache", error, stackTrace); + } + } } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index dba3817b2c..9e5193c8cb 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -99,6 +99,7 @@ class UploadService { } Future manualBackup(List localAssets) async { + await _storageRepository.clearCache(); List tasks = []; for (final asset in localAssets) { final task = await _getUploadTask( @@ -120,6 +121,8 @@ class UploadService { /// Build the upload tasks /// Enqueue the tasks Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { + await _storageRepository.clearCache(); + shouldAbortQueuingTasks = false; final candidates = await _backupRepository.getCandidates(userId); @@ -159,6 +162,7 @@ class UploadService { Future cancelBackup() async { shouldAbortQueuingTasks = true; + await _storageRepository.clearCache(); await _uploadRepository.reset(kBackupGroup); await _uploadRepository.deleteDatabaseRecords(kBackupGroup); diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 12a3c51e8e..8916fdd92b 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; import 'package:path/path.dart' as path; @@ -104,6 +105,10 @@ class BetaSyncSettings extends HookConsumerWidget { } } + Future clearFileCache() async { + await ref.read(storageRepositoryProvider).clearCache(); + } + return FutureBuilder>( future: loadCounts(), builder: (context, snapshot) { @@ -241,6 +246,14 @@ class BetaSyncSettings extends HookConsumerWidget { const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "clear_file_cache".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.playlist_remove_rounded), + onTap: clearFileCache, + ), ListTile( title: Text( "export_database".t(context: context), diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 1534b2e914..7969131e7f 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -40,6 +40,7 @@ void main() { registerFallbackValue(LocalAssetStub.image1); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockStorageRepo.clearCache()).thenAnswer((_) async => {}); }); group('HashService hashAssets', () { From 86d31d7d291555d5ab1a38d3305ff1cff6ee4736 Mon Sep 17 00:00:00 2001 From: Peter Ombodi Date: Wed, 30 Jul 2025 22:33:55 +0300 Subject: [PATCH 153/169] fix(download): handle completed downloads and refresh albums (#20241) * fix(download): handle completed downloads and refresh albums * fix(download): remove use of outdated AlbumService --------- Co-authored-by: Peter Ombodi --- .../infrastructure/action.provider.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 69c0532303..80e27b5970 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,10 +1,13 @@ +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; @@ -30,6 +33,7 @@ class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; late UploadService _uploadService; + late DownloadService _downloadService; ActionNotifier() : super(); @@ -37,6 +41,29 @@ class ActionNotifier extends Notifier { void build() { _uploadService = ref.watch(uploadServiceProvider); _service = ref.watch(actionServiceProvider); + _downloadService = ref.watch(downloadServiceProvider); + _downloadService.onImageDownloadStatus = _downloadImageCallback; + _downloadService.onVideoDownloadStatus = _downloadVideoCallback; + _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; + } + + void _downloadImageCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveImageWithPath(update.task); + } + } + + void _downloadVideoCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveVideo(update.task); + } + } + + void _downloadLivePhotoCallback(TaskStatusUpdate update) async { + if (update.status == TaskStatus.complete) { + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; + _downloadService.saveLivePhotos(update.task, livePhotosId); + } } List _getRemoteIdsForSource(ActionSource source) { From e7d051db3c1a427ce675e304e69f87bbf8bb1a87 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 30 Jul 2025 14:40:13 -0500 Subject: [PATCH 154/169] feat: drift edit time and date action (#20377) * feat: drift edit time and date action * feat: add edit button on asset viewer bottom sheet * update localDateTime column in addition to createdAt to keep consistency * fix: dont update local dateTime Server calcs this anyway and it will be synced when the change is applied. We don't use localDateTime on mobile so there is no reason to update this value * fix: padding around edit icon in ListTile Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> * chore: format * fix: hide date edit control when asset does not have a remote * fix: pull timezones correctly from image --------- Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 17 ++++++++ .../edit_date_time_action_button.widget.dart | 37 +++++++++++++++++- .../asset_viewer/bottom_sheet.widget.dart | 20 +++++++++- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../infrastructure/action.provider.dart | 15 +++++++ .../repositories/asset_api.repository.dart | 4 ++ mobile/lib/services/action.service.dart | 39 +++++++++++++++++++ .../lib/widgets/common/date_time_picker.dart | 2 +- 12 files changed, 136 insertions(+), 7 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index c4cf101b73..700ff60c53 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -835,6 +835,7 @@ "edit_birthday": "Edit Birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", + "edit_date_and_time_action_prompt": "{count} date and time edited", "edit_description": "Edit description", "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 33735f1709..44d7cfb6bb 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -186,6 +186,23 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future updateDateTime(List ids, DateTime dateTime) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteExifEntity, + RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)), + where: (e) => e.assetId.equals(id), + ); + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(createdAt: Value(dateTime)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart index 3db3dde44d..6eeec0658b 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart @@ -1,10 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class EditDateTimeActionButton extends ConsumerWidget { - const EditDateTimeActionButton({super.key}); + final ActionSource source; + + const EditDateTimeActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).editDateTime(source, context); + if (result == null) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'edit_date_and_time_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { @@ -12,6 +46,7 @@ class EditDateTimeActionButton extends ConsumerWidget { maxWidth: 95.0, iconData: Icons.edit_calendar_outlined, label: "control_bottom_app_bar_edit_time".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 17b4cdb214..1d76d3c39d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -143,12 +143,18 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); + Future editDateTime() async { + await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); + } + return SliverList.list( children: [ // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), + trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote ? () async => await editDateTime() : null, ), if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), const SheetPeopleDetails(), @@ -194,11 +200,21 @@ class _AssetDetailBottomSheet extends ConsumerWidget { class _SheetTile extends StatelessWidget { final String title; final Widget? leading; + final Widget? trailing; final String? subtitle; final TextStyle? titleStyle; final TextStyle? subtitleStyle; + final VoidCallback? onTap; - const _SheetTile({required this.title, this.titleStyle, this.leading, this.subtitle, this.subtitleStyle}); + const _SheetTile({ + required this.title, + this.titleStyle, + this.leading, + this.subtitle, + this.subtitleStyle, + this.trailing, + this.onTap, + }); @override Widget build(BuildContext context) { @@ -234,8 +250,10 @@ class _SheetTile extends StatelessWidget { title: titleWidget, titleAlignment: ListTileTitleAlignment.center, leading: leading, + trailing: trailing, contentPadding: leading == null ? null : const EdgeInsets.only(left: 25), subtitle: subtitleWidget, + onTap: onTap, ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 6485926996..45c602935d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -40,7 +40,7 @@ class ArchiveBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index ec0fded6c3..3fb499f2a1 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -40,7 +40,7 @@ class FavoriteBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 3912aef15c..70b2fb00b0 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -76,7 +76,7 @@ class GeneralBottomSheet extends ConsumerWidget { if (multiselect.hasLocal || multiselect.hasMerged) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), ], - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 9765b61684..9f41a0c681 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -43,7 +43,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 80e27b5970..21a22e7e5f 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -266,6 +266,21 @@ class ActionNotifier extends Notifier { } } + Future editDateTime(ActionSource source, BuildContext context) async { + final ids = _getOwnedRemoteIdsForSource(source); + try { + final isEdited = await _service.editDateTime(ids, context); + if (!isEdited) { + return null; + } + + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to edit date and time for assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index bbb176ffa7..07639fbb3a 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -66,6 +66,10 @@ class AssetApiRepository extends ApiRepository { return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); } + Future updateDateTime(List ids, DateTime dateTime) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, dateTimeOriginal: dateTime.toIso8601String())); + } + Future stack(List ids) async { final responseDto = await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index f45071e7f7..9a12745acd 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:maplibre_gl/maplibre_gl.dart' as maplibre; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -159,6 +160,44 @@ class ActionService { return true; } + Future editDateTime(List remoteIds, BuildContext context) async { + DateTime? initialDate; + String? timeZone; + Duration? offset; + + if (remoteIds.length == 1) { + final assetId = remoteIds.first; + final asset = await _remoteAssetRepository.get(assetId); + if (asset == null) { + return false; + } + + final exifData = await _remoteAssetRepository.getExif(assetId); + initialDate = asset.createdAt.toLocal(); + offset = initialDate.timeZoneOffset; + timeZone = exifData?.timeZone; + } + + final dateTime = await showDateTimePicker( + context: context, + initialDateTime: initialDate, + initialTZ: timeZone, + initialTZOffset: offset, + ); + + if (dateTime == null) { + return false; + } + + // convert dateTime to DateTime object + final parsedDateTime = DateTime.parse(dateTime); + + await _assetApiRepository.updateDateTime(remoteIds, parsedDateTime); + await _remoteAssetRepository.updateDateTime(remoteIds, parsedDateTime); + + return true; + } + Future removeFromAlbum(List remoteIds, String albumId) async { int removedCount = 0; final result = await _albumApiRepository.removeAssets(albumId, remoteIds); diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 113462c6c8..9cc8de29ee 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -145,7 +145,7 @@ class _DateTimePicker extends HookWidget { 1, ), trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), - title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium), onTap: pickDate, ), const SizedBox(height: 24), From 27c456eb75333566881da659c22e8085920ceb25 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 30 Jul 2025 14:47:47 -0500 Subject: [PATCH 155/169] fix: people navigation (#20450) --- .../asset_viewer/bottom_sheet/sheet_people_details.widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart index 4cfd95b25c..5adaa7cc72 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -79,7 +79,7 @@ class _SheetPeopleDetailsState extends ConsumerState { context.back(); return; } - context.back(); + context.pop(); context.pushRoute(DriftPersonRoute(person: person)); }, onNameTap: () => showNameEditModal(person), From 6b50d958f4cde887562a8abba4b8aa296557e3d4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 16:50:52 -0300 Subject: [PATCH 156/169] fix: incorrect next/previous action after folder view refresh (#20447) --- .../gallery-viewer/gallery-viewer.svelte | 31 ++++++++++++------- .../[[assetId=id]]/+page.svelte | 4 +-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index 6e0c547b90..30e14abc59 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -27,6 +27,7 @@ import Portal from '../portal/portal.svelte'; interface Props { + initialAssetId?: string; assets: (TimelineAsset | AssetResponseDto)[]; assetInteraction: AssetInteraction; disableAssetSelect?: boolean; @@ -44,6 +45,7 @@ } let { + initialAssetId = undefined, assets = $bindable(), assetInteraction, disableAssetSelect = false, @@ -117,7 +119,14 @@ }; }); - let currentViewAssetIndex = 0; + let currentIndex = 0; + if (initialAssetId && assets.length > 0) { + const index = assets.findIndex(({ id }) => id === initialAssetId); + if (index !== -1) { + currentIndex = index; + } + } + let shiftKeyIsDown = $state(false); let lastAssetMouseEvent: TimelineAsset | null = $state(null); let slidingWindow = $state({ top: 0, bottom: 0 }); @@ -150,8 +159,8 @@ } }); const viewAssetHandler = async (asset: TimelineAsset) => { - currentViewAssetIndex = assets.findIndex((a) => a.id == asset.id); - await setAssetId(assets[currentViewAssetIndex].id); + currentIndex = assets.findIndex((a) => a.id == asset.id); + await setAssetId(assets[currentIndex].id); await navigate({ targetRoute: 'current', assetId: $viewingAsset.id }); }; @@ -324,12 +333,12 @@ if (onNext) { asset = await onNext(); } else { - if (currentViewAssetIndex >= assets.length - 1) { + if (currentIndex >= assets.length - 1) { return false; } - currentViewAssetIndex = currentViewAssetIndex + 1; - asset = currentViewAssetIndex < assets.length ? assets[currentViewAssetIndex] : undefined; + currentIndex = currentIndex + 1; + asset = currentIndex < assets.length ? assets[currentIndex] : undefined; } if (!asset) { @@ -374,12 +383,12 @@ if (onPrevious) { asset = await onPrevious(); } else { - if (currentViewAssetIndex <= 0) { + if (currentIndex <= 0) { return false; } - currentViewAssetIndex = currentViewAssetIndex - 1; - asset = currentViewAssetIndex >= 0 ? assets[currentViewAssetIndex] : undefined; + currentIndex = currentIndex - 1; + asset = currentIndex >= 0 ? assets[currentIndex] : undefined; } if (!asset) { @@ -412,10 +421,10 @@ ); if (assets.length === 0) { await goto(AppRoute.PHOTOS); - } else if (currentViewAssetIndex === assets.length) { + } else if (currentIndex === assets.length) { await handlePrevious(); } else { - await setAssetId(assets[currentViewAssetIndex].id); + await setAssetId(assets[currentIndex].id); } break; } diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index 84ae328ccc..51de0ac1ee 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -21,8 +21,8 @@ import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte'; import Sidebar from '$lib/components/sidebar/sidebar.svelte'; import { AppRoute, QueryParameter } from '$lib/constants'; - import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import type { Viewport } from '$lib/managers/timeline-manager/types'; + import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { foldersStore } from '$lib/stores/folders.svelte'; import { preferences } from '$lib/stores/user.store'; import { cancelMultiselect } from '$lib/utils/asset-utils'; @@ -40,7 +40,6 @@ let { data }: Props = $props(); const viewport: Viewport = $state({ width: 0, height: 0 }); - const assetInteraction = new AssetInteraction(); const handleNavigateToFolder = (folderName: string) => navigateToView(joinPaths(data.tree.path, folderName)); @@ -104,6 +103,7 @@ {#if data.pathAssets && data.pathAssets.length > 0}
Date: Thu, 31 Jul 2025 14:28:45 +0200 Subject: [PATCH 157/169] fix: modal race conditions (#20460) --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index eb4aae708b..14c58a47ee 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.5", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1357,9 +1357,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.5.tgz", - "integrity": "sha512-1wlFMmfDmtGC+Kcc8cYTT00mQaSumR41KEOOOmVn5Rw/8z9pUhpNY8mGl1AxY4qhtnaz+G3dH6vowYzL23D+YQ==", + "version": "0.23.6", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.6.tgz", + "integrity": "sha512-HYIguDx/nCXcvqLKhY1R/+Aks6mn8B9jIiNVQH6WODDPbvGFrvQT5uINhXHrjsdyuzKBVS6dps+lx9+9Z6z4rA==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", diff --git a/web/package.json b/web/package.json index a8d338d547..687263f1a9 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.5", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", From 7391ea6ff9347ca008028e7e6c88d0e06493157c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 11:52:19 -0500 Subject: [PATCH 158/169] chore: large file size grid view styling (#20472) * chore: large file grid styles * chore: large file grid styles --- .../large-assets/large-asset-data.svelte | 55 ++++++------------- .../[[assetId=id]]/+page.svelte | 2 +- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte index 71f3dbb5c4..db1fe4615b 100644 --- a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte +++ b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte @@ -1,12 +1,8 @@
-
- + {#if !!asset.libraryId} +
External
+ {/if}
- -
-
-
{asset.originalFileName}
- {getAssetResolution(asset)} -
-
- {getFileSize(asset, 1)} -
+
+ {asset.originalFileName} +
+
+

{getFileSize(asset, 1)}

diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte index 75ac4fab93..35c101492c 100644 --- a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -56,7 +56,7 @@ -
+
{#if assets && data.assets.length > 0} {#each assets as asset (asset.id)} setAsset(asset)} /> From c3263e50fce9233f843307d3b0963849ad810f13 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:19:26 +0000 Subject: [PATCH 159/169] chore: version v1.137.0 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 2b20af801b..90304a8b12 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index a20ae145b4..5ff2a9a522 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 6018e26011..2fb214b824 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.0", + "url": "https://v1.137.0.archive.immich.app" + }, { "label": "v1.136.0", "url": "https://v1.136.0.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 8c1e6e2ba1..ae5d9164b2 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 988f1dfd96..a607e30d96 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 2287ab0821..06d0d2cc02 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.136.0", + "android.injected.version.name" => "1.137.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 70fade71ba..e037eb08c7 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.136.0" + version_number: "1.137.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 058524479c..140d1fa44c 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.136.0 +- API version: 1.137.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 8510537a1f..5fc1736f54 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.136.0+3000 +version: 1.137.0+3001 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 9dc7540dd8..b031f65498 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.136.0", + "version": "1.137.0", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index e906674878..6aaea1d8f5 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index a5582ee60a..9495235d23 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 53a2f4b144..1151cbfc08 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.136.0 + * 1.137.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index bac0540e38..a314b20872 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 2a5fa5ea8f..82e96b6b51 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 14c58a47ee..57d264c4d2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 687263f1a9..52e55d4619 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 3cdc6844a13d11f00bfc21684196e1c9f5ceb221 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:58:35 +0200 Subject: [PATCH 160/169] fix: automatic media location migration without internal assets (#20489) --- server/src/queries/asset.repository.sql | 23 +++------------------ server/src/repositories/asset.repository.ts | 16 +------------- server/src/services/cli.service.ts | 7 +------ server/src/services/storage.service.ts | 6 +++--- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 185afa562b..f7a4d1402d 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -170,27 +170,10 @@ where -- AssetRepository.getFileSamples select - "asset"."id", - "asset"."originalPath", - "asset"."sidecarPath", - "asset"."encodedVideoPath", - ( - select - coalesce(json_agg(agg), '[]') - from - ( - select - "path" - from - "asset_file" - where - "asset"."id" = "asset_file"."assetId" - ) as agg - ) as "files" + "assetId", + "path" from - "asset" -where - "asset"."libraryId" is null + "asset_file" limit 3 diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 286a66e9af..8aa25c4a6a 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; -import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; @@ -338,20 +337,7 @@ export class AssetRepository { @GenerateSql() getFileSamples() { - return this.db - .selectFrom('asset') - .select((eb) => [ - 'asset.id', - 'asset.originalPath', - 'asset.sidecarPath', - 'asset.encodedVideoPath', - jsonArrayFrom(eb.selectFrom('asset_file').select('path').whereRef('asset.id', '=', 'asset_file.assetId')).as( - 'files', - ), - ]) - .where('asset.libraryId', 'is', null) - .limit(sql.lit(3)) - .execute(); + return this.db.selectFrom('asset_file').select(['assetId', 'path']).limit(sql.lit(3)).execute(); } @GenerateSql({ params: [DummyValue.UUID] }) diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 674b885dc4..38144e95b4 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -86,12 +86,7 @@ export class CliService extends BaseService { } for (const asset of assets) { - paths.push( - asset.originalPath, - asset.sidecarPath, - asset.encodedVideoPath, - ...asset.files.map((file) => file.path), - ); + paths.push(asset.path); } return paths.filter(Boolean) as string[]; diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index cd8910305a..bcfb34535c 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -97,18 +97,18 @@ export class StorageService extends BaseService { const current = StorageCore.getMediaLocation(); const samples = await this.assetRepository.getFileSamples(); if (samples.length > 0) { - const originalPath = samples[0].originalPath; + const path = samples[0].path; const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); let previous = savedValue?.location || ''; if (!previous) { - previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; + previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } if (previous !== current) { this.logger.log(`Media location changed (from=${previous}, to=${current})`); - if (!originalPath.startsWith(previous)) { + if (!path.startsWith(previous)) { throw new Error( 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', ); From a07531be3b27416aeec9ee0630672fd8d1949556 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 23:05:34 +0000 Subject: [PATCH 161/169] chore: version v1.137.1 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 90304a8b12..d27a7371a4 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index 5ff2a9a522..b8db8cbd06 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 2fb214b824..d41f1a7bc4 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.1", + "url": "https://v1.137.1.archive.immich.app" + }, { "label": "v1.137.0", "url": "https://v1.137.0.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index ae5d9164b2..473748d065 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index a607e30d96..d736a9201f 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 06d0d2cc02..35af2a1e1f 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.137.0", + "android.injected.version.name" => "1.137.1", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index e037eb08c7..c3eaf44cfc 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.0" + version_number: "1.137.1" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 140d1fa44c..8bd08e4d13 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.0 +- API version: 1.137.1 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 5fc1736f54..d3448d7f1e 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.0+3001 +version: 1.137.1+3001 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index b031f65498..1288c95df8 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.0", + "version": "1.137.1", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 6aaea1d8f5..bb49b1060f 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 9495235d23..415695de62 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 1151cbfc08..6a8ee07b4f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.0 + * 1.137.1 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index a314b20872..f59c267496 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 82e96b6b51..1a00b2c243 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 57d264c4d2..1f4769083b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 52e55d4619..ad33a36a0f 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 4bd465e7525f750cdd98296775cf5f27f2fec67f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:02:28 -0500 Subject: [PATCH 162/169] feat: change grid size with gesture (#20455) --- .../widgets/timeline/timeline.widget.dart | 121 +++++++++++++----- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index d946872781..dcf2c74ed5 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -3,6 +3,7 @@ import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -88,10 +89,23 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final _scrollController = ScrollController(); StreamSubscription? _eventSubscription; + int _perRow = 4; + double _scaleFactor = 3.0; + double _baseScaleFactor = 3.0; + @override void initState() { super.initState(); _eventSubscription = EventStream.shared.listen(_onEvent); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + setState(() { + _perRow = currentTilesPerRow; + _scaleFactor = 7.0 - _perRow; + _baseScaleFactor = _scaleFactor; + }); + }); } void _onEvent(Event event) { @@ -177,43 +191,72 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { return PrimaryScrollController( controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], - ), + child: RawGestureDetector( + gestures: { + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + _baseScaleFactor = _scaleFactor; + }; + + scale.onUpdate = (details) { + final newScaleFactor = math.max(math.min(5.0, _baseScaleFactor * details.scale), 1.0); + final newPerRow = 7 - newScaleFactor.toInt(); + + if (newPerRow != _perRow) { + setState(() { + _scaleFactor = newScaleFactor; + _perRow = newPerRow; + }); + + ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); + } + }; + }, ), - if (!isSelectionMode && isMultiSelectEnabled) ...[ - const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - if (widget.bottomSheet != null) widget.bottomSheet!, + }, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else if (widget.appBar != null) + widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), + ), + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], - ], + ), ), ); }, @@ -443,3 +486,11 @@ class _MultiSelectStatusButton extends ConsumerWidget { ); } } + +/// accepts a gesture even though it should reject it (because child won) +class CustomScaleGestureRecognizer extends ScaleGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} From 1378f223682d6cccc4cd9d94b19e16ab59e088b8 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:28:33 -0500 Subject: [PATCH 163/169] fix: add to album render empty app bar (#20480) * fix: add to album render empty app bar * set current album --- .../drift_backup_album_selection.page.dart | 37 +++++++++---------- .../pages/drift_remote_album.page.dart | 29 +++++++++------ .../widgets/album/album_selector.widget.dart | 2 + 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 396b711d07..865845525a 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; @@ -14,7 +13,6 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -67,14 +65,14 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState album.backupSelection == BackupSelection.selected).toList(); final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList(); - handleSyncAlbumToggle(bool isEnable) async { - if (isEnable) { - await ref.read(albumProvider.notifier).refreshRemoteAlbums(); - for (final album in selectedBackupAlbums) { - await ref.read(albumProvider.notifier).createSyncAlbum(album.name); - } - } - } + // handleSyncAlbumToggle(bool isEnable) async { + // if (isEnable) { + // await ref.read(albumProvider.notifier).refreshRemoteAlbums(); + // for (final album in selectedBackupAlbums) { + // await ref.read(albumProvider.notifier).createSyncAlbum(album.name); + // } + // } + // } return PopScope( onPopInvokedWithResult: (didPop, result) async { @@ -167,16 +165,15 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState { + late RemoteAlbum _album; @override void initState() { super.initState(); + _album = widget.album; } Future addAssets(BuildContext context) async { - final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); + final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(_album.id); final newAssets = await context.pushRoute>( DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), @@ -47,7 +49,7 @@ class _RemoteAlbumPageState extends ConsumerState { final added = await ref .read(remoteAlbumProvider.notifier) .addAssets( - widget.album.id, + _album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; return remoteAsset.id; @@ -64,14 +66,14 @@ class _RemoteAlbumPageState extends ConsumerState { } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: widget.album)); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: _album)); if (newUsers == null || newUsers.isEmpty) { return; } try { - await ref.read(remoteAlbumProvider.notifier).addUsers(widget.album.id, newUsers); + await ref.read(remoteAlbumProvider.notifier).addUsers(_album.id, newUsers); if (newUsers.isNotEmpty) { ImmichToast.show( @@ -81,7 +83,7 @@ class _RemoteAlbumPageState extends ConsumerState { ); } - ref.invalidate(remoteAlbumSharedUsersProvider(widget.album.id)); + ref.invalidate(remoteAlbumSharedUsersProvider(_album.id)); } catch (e) { ImmichToast.show( context: context, @@ -92,7 +94,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(_album.id); ref.invalidate(timelineServiceProvider); } @@ -106,7 +108,7 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text('album_delete_confirmation'.t(context: context, args: {'album': widget.album.name})), + Text('album_delete_confirmation'.t(context: context, args: {'album': _album.name})), const SizedBox(height: 8), Text('album_delete_confirmation_description'.t(context: context)), ], @@ -128,7 +130,7 @@ class _RemoteAlbumPageState extends ConsumerState { if (confirmed == true) { try { - await ref.read(remoteAlbumProvider.notifier).deleteAlbum(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(_album.id); ImmichToast.show( context: context, @@ -151,17 +153,20 @@ class _RemoteAlbumPageState extends ConsumerState { final result = await showDialog<_EditAlbumData?>( context: context, barrierDismissible: true, - builder: (context) => _EditAlbumDialog(album: widget.album), + builder: (context) => _EditAlbumDialog(album: _album), ); if (result != null && context.mounted) { + setState(() { + _album = _album.copyWith(name: result.name, description: result.description ?? ''); + }); HapticFeedback.mediumImpact(); } } void showOptionSheet(BuildContext context) { final user = ref.watch(currentUserProvider); - final isOwner = user != null ? user.id == widget.album.ownerId : false; + final isOwner = user != null ? user.id == _album.ownerId : false; showModalBottomSheet( context: context, @@ -205,7 +210,7 @@ class _RemoteAlbumPageState extends ConsumerState { return ProviderScope( overrides: [ timelineServiceProvider.overrideWith((ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); ref.onDispose(timelineService.dispose); return timelineService; }), @@ -217,7 +222,7 @@ class _RemoteAlbumPageState extends ConsumerState { onToggleAlbumOrder: () => toggleAlbumOrder(), onEditTitle: () => showEditTitleAndDescription(context), ), - bottomSheet: RemoteAlbumBottomSheet(album: widget.album), + bottomSheet: RemoteAlbumBottomSheet(album: _album), ), ); } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index cb6a38041d..2eec620ec4 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -578,6 +579,7 @@ class AddToAlbumHeader extends ConsumerWidget { return; } + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); context.pushRoute(RemoteAlbumRoute(album: newAlbum)); } From c5f14adff035328dd3af04b00318b07c560b5909 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:29:01 -0500 Subject: [PATCH 164/169] feat: drag to select beta timeline (#20456) --- .../widgets/timeline/fixed/segment.model.dart | 13 +- .../widgets/timeline/timeline.widget.dart | 159 ++++++++++--- .../timeline/timeline_drag_region.dart | 212 ++++++++++++++++++ 3 files changed, 343 insertions(+), 41 deletions(-) create mode 100644 mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index a5f0c19eb8..05f96d49de 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -1,7 +1,7 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; @@ -11,6 +11,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -125,10 +126,14 @@ class _FixedSegmentRow extends ConsumerWidget { textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), - asset: assets[i], + TimelineAssetIndexWrapper( assetIndex: assetIndex + i, + segmentIndex: 0, // For simplicity, using 0 for now + child: _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ), ], ); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index dcf2c74ed5..838edd8a47 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:math' as math; import 'package:collection/collection.dart'; @@ -7,6 +8,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; @@ -16,6 +18,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_s import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -89,6 +92,12 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final _scrollController = ScrollController(); StreamSubscription? _eventSubscription; + // Drag selection state + bool _dragging = false; + TimelineAssetIndex? _dragAnchorIndex; + final Set _draggedAssets = HashSet(); + ScrollPhysics? _scrollPhysics; + int _perRow = 4; double _scaleFactor = 3.0; double _baseScaleFactor = 3.0; @@ -164,6 +173,71 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }); } + // Drag selection methods + void _setDragStartIndex(TimelineAssetIndex index) { + setState(() { + _scrollPhysics = const ClampingScrollPhysics(); + _dragAnchorIndex = index; + _dragging = true; + }); + } + + void _stopDrag() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Update the physics post frame to prevent sudden change in physics on iOS. + setState(() { + _scrollPhysics = null; + }); + }); + setState(() { + _dragging = false; + _draggedAssets.clear(); + }); + // Reset the scrolling state after a small delay to allow bottom sheet to expand again + Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) { + ref.read(timelineStateProvider.notifier).setScrolling(false); + } + }); + } + + void _dragScroll(ScrollDirection direction) { + _scrollController.animateTo( + _scrollController.offset + (direction == ScrollDirection.forward ? 175 : -175), + duration: const Duration(milliseconds: 125), + curve: Curves.easeOut, + ); + } + + void _handleDragAssetEnter(TimelineAssetIndex index) { + if (_dragAnchorIndex == null || !_dragging) return; + + final timelineService = ref.read(timelineServiceProvider); + final dragAnchorIndex = _dragAnchorIndex!; + + // Calculate the range of assets to select + final startIndex = math.min(dragAnchorIndex.assetIndex, index.assetIndex); + final endIndex = math.max(dragAnchorIndex.assetIndex, index.assetIndex); + final count = endIndex - startIndex + 1; + + // Load the assets in the range + if (timelineService.hasRange(startIndex, count)) { + final selectedAssets = timelineService.getAssets(startIndex, count); + + // Clear previous drag selection and add new range + final multiSelectNotifier = ref.read(multiSelectProvider.notifier); + for (final asset in _draggedAssets) { + multiSelectNotifier.deselectAsset(asset); + } + _draggedAssets.clear(); + + for (final asset in selectedAssets) { + multiSelectNotifier.selectAsset(asset); + _draggedAssets.add(asset); + } + } + } + @override Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); @@ -216,46 +290,57 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }, ), }, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) - const SelectionSliverAppBar() - else if (widget.appBar != null) - widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, + child: TimelineDragRegion( + onStart: _setDragStartIndex, + onAssetEnter: _handleDragAssetEnter, + onEnd: _stopDrag, + onScroll: _dragScroll, + onScrollStart: () { + // Minimize the bottom sheet when drag selection starts + ref.read(timelineStateProvider.notifier).setScrolling(true); + }, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + physics: _scrollPhysics, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else if (widget.appBar != null) + widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), ), - ), - if (!isSelectionMode && isMultiSelectEnabled) ...[ - const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - if (widget.bottomSheet != null) widget.bottomSheet!, + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], - ], + ), ), ), ); diff --git a/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart new file mode 100644 index 0000000000..88d46b143f --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart @@ -0,0 +1,212 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class TimelineDragRegion extends StatefulWidget { + final Widget child; + + final void Function(TimelineAssetIndex valueKey)? onStart; + final void Function(TimelineAssetIndex valueKey)? onAssetEnter; + final void Function()? onEnd; + final void Function()? onScrollStart; + final void Function(ScrollDirection direction)? onScroll; + + const TimelineDragRegion({ + super.key, + required this.child, + this.onStart, + this.onAssetEnter, + this.onEnd, + this.onScrollStart, + this.onScroll, + }); + + @override + State createState() => _TimelineDragRegionState(); +} + +class _TimelineDragRegionState extends State { + late TimelineAssetIndex? assetUnderPointer; + late TimelineAssetIndex? anchorAsset; + + // Scroll related state + static const double scrollOffset = 0.10; + double? topScrollOffset; + double? bottomScrollOffset; + Timer? scrollTimer; + late bool scrollNotified; + + @override + void initState() { + super.initState(); + assetUnderPointer = null; + anchorAsset = null; + scrollNotified = false; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + topScrollOffset = null; + bottomScrollOffset = null; + } + + @override + void dispose() { + scrollTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RawGestureDetector( + gestures: { + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( + () => _CustomLongPressGestureRecognizer(), + _registerCallbacks, + ), + }, + child: widget.child, + ); + } + + void _registerCallbacks(_CustomLongPressGestureRecognizer recognizer) { + recognizer.onLongPressMoveUpdate = (details) => _onLongPressMove(details); + recognizer.onLongPressStart = (details) => _onLongPressStart(details); + recognizer.onLongPressUp = _onLongPressEnd; + } + + TimelineAssetIndex? _getValueKeyAtPosition(Offset position) { + final box = context.findAncestorRenderObjectOfType(); + if (box == null) return null; + + final hitTestResult = BoxHitTestResult(); + final local = box.globalToLocal(position); + if (!box.hitTest(hitTestResult, position: local)) return null; + + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target + as _TimelineAssetIndexProxy?) + ?.index; + } + + void _onLongPressStart(LongPressStartDetails event) { + /// Calculate widget height and scroll offset when long press starting instead of in [initState] + /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size + final height = context.size?.height; + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { + topScrollOffset = height * scrollOffset; + bottomScrollOffset = height - topScrollOffset!; + } + + final initialHit = _getValueKeyAtPosition(event.globalPosition); + anchorAsset = initialHit; + if (initialHit == null) return; + + if (anchorAsset != null) { + widget.onStart?.call(anchorAsset!); + } + } + + void _onLongPressEnd() { + scrollNotified = false; + scrollTimer?.cancel(); + widget.onEnd?.call(); + } + + void _onLongPressMove(LongPressMoveUpdateDetails event) { + if (anchorAsset == null) return; + if (topScrollOffset == null || bottomScrollOffset == null) return; + + final currentDy = event.localPosition.dy; + + if (currentDy > bottomScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.forward), + ); + } else if (currentDy < topScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.reverse), + ); + } else { + scrollTimer?.cancel(); + scrollTimer = null; + } + + final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition); + if (currentlyTouchingAsset == null) return; + + if (assetUnderPointer != currentlyTouchingAsset) { + if (!scrollNotified) { + scrollNotified = true; + widget.onScrollStart?.call(); + } + + widget.onAssetEnter?.call(currentlyTouchingAsset); + assetUnderPointer = currentlyTouchingAsset; + } + } +} + +class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} + +class TimelineAssetIndexWrapper extends SingleChildRenderObjectWidget { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndexWrapper({ + required Widget super.child, + required this.assetIndex, + required this.segmentIndex, + super.key, + }); + + @override + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy createRenderObject(BuildContext context) { + return _TimelineAssetIndexProxy( + index: TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex), + ); + } + + @override + void updateRenderObject( + BuildContext context, + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy renderObject, + ) { + renderObject.index = TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex); + } +} + +class _TimelineAssetIndexProxy extends RenderProxyBox { + TimelineAssetIndex index; + + _TimelineAssetIndexProxy({required this.index}); +} + +class TimelineAssetIndex { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndex({required this.assetIndex, required this.segmentIndex}); + + @override + bool operator ==(covariant TimelineAssetIndex other) { + if (identical(this, other)) return true; + + return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex; + } + + @override + int get hashCode => assetIndex.hashCode ^ segmentIndex.hashCode; +} From 9242afb4b01137cdb031f73a0e361f35d70b539e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:45:16 +0000 Subject: [PATCH 165/169] chore: version v1.137.2 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index d27a7371a4..777789eb51 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index b8db8cbd06..d7147a0f36 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index d41f1a7bc4..e64d8c8b35 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.2", + "url": "https://v1.137.2.archive.immich.app" + }, { "label": "v1.137.1", "url": "https://v1.137.1.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 473748d065..7c05316f86 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index d736a9201f..1d6d757306 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 35af2a1e1f..e693eeb491 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.137.1", + "android.injected.version.name" => "1.137.2", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index c3eaf44cfc..704e7c8590 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.1" + version_number: "1.137.2" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 8bd08e4d13..395e81227e 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.1 +- API version: 1.137.2 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index d3448d7f1e..22644ffcbc 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.1+3001 +version: 1.137.2+3002 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 1288c95df8..7b503e2253 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.1", + "version": "1.137.2", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index bb49b1060f..fe9f3d4485 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 415695de62..351345926a 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 6a8ee07b4f..cb4f6b7f8f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.1 + * 1.137.2 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index f59c267496..01db436776 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 1a00b2c243..19f0d8e344 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 1f4769083b..c687883ae7 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index ad33a36a0f..57196bc6f9 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 1b8354ed36a1910898c3fcb5588be78782f02206 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 1 Aug 2025 05:38:52 -0500 Subject: [PATCH 166/169] chore: post release tasks (#20497) --- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/Runner.xcodeproj/project.pbxproj | 18 +++++++++--------- mobile/ios/Runner/Info.plist | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index e693eeb491..09e72dfa25 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,7 +35,7 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 205, + "android.injected.version.code" => 3002, "android.injected.version.name" => "1.137.2", } ) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index fb0908e8b6..218a294c33 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -649,7 +649,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -793,7 +793,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -823,7 +823,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -857,7 +857,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -900,7 +900,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -940,7 +940,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -979,7 +979,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1023,7 +1023,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1064,7 +1064,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 4237813dfc..d3e42b9939 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.135.1 + 1.137.2 CFBundleSignature ???? CFBundleURLTypes @@ -105,7 +105,7 @@ CFBundleVersion - 210 + 213 FLTEnableImpeller ITSAppUsesNonExemptEncryption From 8108f50c4e0e5ef02b0e9dbf353ff67fd22ed28d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:09:59 +0530 Subject: [PATCH 167/169] fix: guard IS_FAVORITE column with SDK check (#20511) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../alextran/immich/sync/MessagesImplBase.kt | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index d7073e7cfc..b2ceb8a9f2 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -29,21 +29,24 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString() ) const val BUCKET_SELECTION = "(${MediaStore.Files.FileColumns.BUCKET_ID} = ?)" - val ASSET_PROJECTION = arrayOf( - MediaStore.MediaColumns._ID, - MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, - MediaStore.MediaColumns.DATE_TAKEN, - MediaStore.MediaColumns.DATE_ADDED, - MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.Files.FileColumns.MEDIA_TYPE, - MediaStore.MediaColumns.BUCKET_ID, - MediaStore.MediaColumns.WIDTH, - MediaStore.MediaColumns.HEIGHT, - MediaStore.MediaColumns.DURATION, - MediaStore.MediaColumns.ORIENTATION, - MediaStore.MediaColumns.IS_FAVORITE, - ) + val ASSET_PROJECTION = buildList { + add(MediaStore.MediaColumns._ID) + add(MediaStore.MediaColumns.DATA) + add(MediaStore.MediaColumns.DISPLAY_NAME) + add(MediaStore.MediaColumns.DATE_TAKEN) + add(MediaStore.MediaColumns.DATE_ADDED) + add(MediaStore.MediaColumns.DATE_MODIFIED) + add(MediaStore.Files.FileColumns.MEDIA_TYPE) + add(MediaStore.MediaColumns.BUCKET_ID) + add(MediaStore.MediaColumns.WIDTH) + add(MediaStore.MediaColumns.HEIGHT) + add(MediaStore.MediaColumns.DURATION) + add(MediaStore.MediaColumns.ORIENTATION) + // IS_FAVORITE is only available on Android 11 and above + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + add(MediaStore.MediaColumns.IS_FAVORITE) + } + }.toTypedArray() const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 } @@ -78,7 +81,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) - val favoriteColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.IS_FAVORITE) + val favoriteColumn = c.getColumnIndex(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -107,7 +110,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) - val isFavorite = c.getInt(favoriteColumn) != 0; + val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 val asset = PlatformAsset( id, From 4d5cd1a6b50a0575567a4d7f4d1d65a06597d73a Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:49:51 +0200 Subject: [PATCH 168/169] fix: migration if media location is set (#20532) --- server/src/services/storage.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index bcfb34535c..5b08204bf9 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -101,6 +101,10 @@ export class StorageService extends BaseService { const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); let previous = savedValue?.location || ''; + if (!previous && this.configRepository.getEnv().storage.mediaLocation) { + previous = current; + } + if (!previous) { previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } From 007ba1d9efdc571c87ca1553dd28dbbfe7eeee2f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:52:24 +0000 Subject: [PATCH 169/169] chore: version v1.137.3 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 777789eb51..1271247865 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index d7147a0f36..a5c1b19159 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index e64d8c8b35..04c6604a14 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.3", + "url": "https://v1.137.3.archive.immich.app" + }, { "label": "v1.137.2", "url": "https://v1.137.2.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 7c05316f86..319f1b34b3 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 1d6d757306..8b8540e9ba 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 09e72dfa25..8fd2ba3059 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 3002, - "android.injected.version.name" => "1.137.2", + "android.injected.version.name" => "1.137.3", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 704e7c8590..19f00c5f43 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.2" + version_number: "1.137.3" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 395e81227e..c4349ff657 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.2 +- API version: 1.137.3 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 22644ffcbc..020ce4676e 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.2+3002 +version: 1.137.3+3002 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 7b503e2253..d97585a39e 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.2", + "version": "1.137.3", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index fe9f3d4485..a9b5c295ca 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 351345926a..f01ec302ce 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index cb4f6b7f8f..d26d14aa4a 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.2 + * 1.137.3 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index 01db436776..ed2dc2e14f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 19f0d8e344..472b746630 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index c687883ae7..cfd65b63d8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 57196bc6f9..bb17955b08 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": {