merge main

This commit is contained in:
shenlong-tanwen
2025-09-17 16:08:34 +05:30
528 changed files with 17365 additions and 6390 deletions
@@ -171,7 +171,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
album.activityEnabled = value;
}
},
activeColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
dense: true,
title: Text(
"comments_and_likes",
@@ -205,9 +205,9 @@ 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"),
return ListTile(
leading: const Icon(Icons.info_outline_rounded),
title: Text('background_backup_running_error'.tr()),
);
}
+54 -10
View File
@@ -5,15 +5,19 @@ 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/album/local_album.model.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/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/app_settings.provider.dart';
import 'package:immich_mobile/providers/background_sync.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/routing/router.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
@RoutePage()
@@ -25,6 +29,8 @@ class DriftBackupPage extends ConsumerStatefulWidget {
}
class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
Timer? _countPoller;
@override
void initState() {
super.initState();
@@ -33,22 +39,40 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
return;
}
if (ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup)) {
_startCountPolling();
}
ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
}
Future<void> startBackup() async {
final currentUser = ref.read(currentUserProvider);
if (currentUser == null) {
return;
}
void _startCountPolling() {
_countPoller?.cancel();
_countPoller = Timer.periodic(const Duration(seconds: 5), (timer) async {
if (!mounted) {
timer.cancel();
return;
}
await ref.read(backgroundSyncProvider).syncRemote();
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
await ref.read(driftBackupProvider.notifier).startBackup(currentUser.id);
final currentUser = ref.read(currentUserProvider);
if (currentUser == null) {
timer.cancel();
return;
}
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
});
}
Future<void> stopBackup() async {
await ref.read(driftBackupProvider.notifier).cancel();
void _stopCountPolling() {
_countPoller?.cancel();
_countPoller = null;
}
@override
void dispose() {
_stopCountPolling();
super.dispose();
}
@override
@@ -58,6 +82,26 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
.where((album) => album.backupSelection == BackupSelection.selected)
.toList();
final backupNotifier = ref.read(driftBackupProvider.notifier);
final backgroundManager = ref.read(backgroundSyncProvider);
Future<void> startBackup() async {
final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser == null) {
return;
}
await backgroundManager.syncRemote();
await backupNotifier.getBackupStatus(currentUser.id);
await backupNotifier.startBackup(currentUser.id);
_startCountPolling();
}
Future<void> stopBackup() async {
await backupNotifier.cancel();
_stopCountPolling();
}
return Scaffold(
appBar: AppBar(
elevation: 0,
@@ -1,6 +1,7 @@
import 'dart:io';
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/album/local_album.model.dart';
@@ -249,7 +250,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
mainAxisSize: MainAxisSize.max,
children: [
const CircularProgressIndicator(strokeWidth: 4),
Text("Creating linked albums...", style: context.textTheme.labelLarge),
Text('creating_linked_albums'.tr(), style: context.textTheme.labelLarge),
],
),
),
@@ -1,4 +1,5 @@
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/asset/base_asset.model.dart';
@@ -58,8 +59,10 @@ class DriftBackupAssetDetailPage extends ConsumerWidget {
overflow: TextOverflow.ellipsis,
);
},
error: (error, stackTrace) =>
Text('Error: $error', style: TextStyle(color: context.colorScheme.error)),
error: (error, stackTrace) => Text(
'error_saving_image'.tr(args: [error.toString()]),
style: TextStyle(color: context.colorScheme.error),
),
loading: () => const SizedBox(height: 16, width: 16, child: CircularProgressIndicator.adaptive()),
),
],
@@ -83,7 +86,7 @@ class DriftBackupAssetDetailPage extends ConsumerWidget {
);
},
error: (Object error, StackTrace stackTrace) {
return Center(child: Text('Error: $error'));
return Center(child: Text('error_saving_image'.tr(args: [error.toString()])));
},
loading: () {
return const SizedBox(height: 48, width: 48, child: Center(child: CircularProgressIndicator.adaptive()));
+2 -2
View File
@@ -1,4 +1,5 @@
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';
@@ -8,7 +9,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/immich_logger.service.dart';
import 'package:intl/intl.dart';
@RoutePage()
class AppLogPage extends HookConsumerWidget {
@@ -49,7 +49,7 @@ class AppLogPage extends HookConsumerWidget {
return Scaffold(
appBar: AppBar(
title: const Text("Logs", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)),
title: Text('logs'.tr(), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)),
scrolledUnderElevation: 1,
elevation: 2,
actions: [
@@ -1,4 +1,5 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -36,7 +37,7 @@ class AppLogDetailPage extends HookConsumerWidget {
context.scaffoldMessenger.showSnackBar(
SnackBar(
content: Text(
"Copied to clipboard",
"copied_to_clipboard".tr(),
style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor),
),
),
@@ -97,7 +98,7 @@ class AppLogDetailPage extends HookConsumerWidget {
}
return Scaffold(
appBar: AppBar(title: const Text("Log Detail")),
appBar: AppBar(title: Text("log_detail_title".tr())),
body: SafeArea(
child: ListView(
children: [
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -13,6 +15,7 @@ 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/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:immich_mobile/services/background.service.dart';
@@ -41,49 +44,13 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
Future<void> _handleMigration() async {
try {
if (widget.switchingToBeta) {
final assetNotifier = ref.read(assetProvider.notifier);
if (assetNotifier.mounted) {
assetNotifier.dispose();
}
final albumNotifier = ref.read(albumProvider.notifier);
if (albumNotifier.mounted) {
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();
final permission = await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission();
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 migrateStoreToSqlite(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).disableService();
}
} else {
await ref.read(backgroundSyncProvider).cancel();
ref.read(websocketProvider.notifier).stopListeningToBetaEvents();
ref.read(websocketProvider.notifier).startListeningToOldEvents();
ref.read(readonlyModeProvider.notifier).setReadonlyMode(false);
await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
await ref.read(driftBackgroundUploadFgService).disable();
}
await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
await DriftStoreRepository(ref.read(driftProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
await _performMigrationLogic().timeout(
const Duration(minutes: 3),
onTimeout: () async {
await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
await DriftStoreRepository(ref.read(driftProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
},
);
if (mounted) {
setState(() {
@@ -101,6 +68,54 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
}
}
Future<void> _performMigrationLogic() async {
if (widget.switchingToBeta) {
final assetNotifier = ref.read(assetProvider.notifier);
if (assetNotifier.mounted) {
assetNotifier.dispose();
}
final albumNotifier = ref.read(albumProvider.notifier);
if (albumNotifier.mounted) {
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();
await ref.read(driftProvider).reset();
await Store.put(StoreKey.shouldResetSync, true);
final permission = await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission();
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 migrateStoreToSqlite(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).disableService();
}
} else {
await ref.read(backgroundSyncProvider).cancel();
ref.read(websocketProvider.notifier).stopListeningToBetaEvents();
ref.read(websocketProvider.notifier).startListeningToOldEvents();
ref.read(readonlyModeProvider.notifier).setReadonlyMode(false);
await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
await ref.read(backgroundWorkerFgServiceProvider).disable();
}
await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
await DriftStoreRepository(ref.read(driftProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);
}
@override
Widget build(BuildContext context) {
return Scaffold(
+17 -29
View File
@@ -11,8 +11,7 @@ import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_se
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/beta_sync_settings/sync_status_and_actions.dart';
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';
@@ -20,7 +19,6 @@ 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_settings_subtitle"),
@@ -28,14 +26,14 @@ enum SettingSection {
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");
timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"),
beta('sync_status', Icons.sync_outlined, "sync_status_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 =>
@@ -45,6 +43,7 @@ enum SettingSection {
SettingSection.notifications => const NotificationSetting(),
SettingSection.preferences => const PreferenceSetting(),
SettingSection.timeline => const AssetListSettings(),
SettingSection.beta => const SyncStatusAndActions(),
};
const SettingSection(this.title, this.icon, this.subtitle);
@@ -59,7 +58,7 @@ class SettingsPage extends StatelessWidget {
context.locale;
return Scaffold(
appBar: AppBar(centerTitle: false, title: const Text('settings').tr()),
body: context.isMobile ? const _MobileLayout() : const _TabletLayout(),
body: context.isMobile ? const SafeArea(child: _MobileLayout()) : const SafeArea(child: _TabletLayout()),
);
}
}
@@ -72,13 +71,12 @@ class _MobileLayout extends StatelessWidget {
.expand(
(setting) => setting == SettingSection.beta
? [
const BetaTimelineListTile(),
if (Store.isBetaTimelineEnabled)
SettingsCard(
icon: Icons.sync_outlined,
title: 'beta_sync'.tr(),
subtitle: 'beta_sync_subtitle'.tr(),
settingRoute: const BetaSyncSettingsRoute(),
title: 'sync_status'.tr(),
subtitle: 'sync_status_subtitle'.tr(),
settingRoute: const SyncStatusRoute(),
),
]
: [
@@ -93,7 +91,7 @@ class _MobileLayout extends StatelessWidget {
.toList();
return ListView(
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(top: 10.0, bottom: 56),
padding: const EdgeInsets.only(top: 10.0, bottom: 16),
children: [...settings],
);
}
@@ -134,21 +132,6 @@ class _TabletLayout extends HookWidget {
}
}
class _BetaLandscapeToggle extends HookWidget {
const _BetaLandscapeToggle();
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 100, child: BetaTimelineListTile()),
if (Store.isBetaTimelineEnabled) const Expanded(child: BetaSyncSettings()),
],
);
}
}
@RoutePage()
class SettingsSubPage extends StatelessWidget {
const SettingsSubPage(this.section, {super.key});
@@ -158,9 +141,14 @@ class SettingsSubPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
context.locale;
return Scaffold(
appBar: AppBar(centerTitle: false, title: Text(section.title).tr()),
body: section.widget,
return SafeArea(
bottom: true,
top: false,
right: true,
child: Scaffold(
appBar: AppBar(centerTitle: false, title: Text(section.title).tr()),
body: section.widget,
),
);
}
}
+60 -35
View File
@@ -1,14 +1,12 @@
import 'dart:async';
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/domain/utils/isolate_lock_manager.dart';
import 'package:immich_mobile/entities/store.entity.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/gallery_permission.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
@@ -29,19 +27,11 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
@override
void initState() {
super.initState();
final lockManager = ref.read(isolateLockManagerProvider(kIsolateLockManagerPort));
lockManager.requestHolderToClose();
lockManager
.acquireLock()
.timeout(const Duration(seconds: 5))
.whenComplete(
() => ref
.read(authProvider.notifier)
.setOpenApiServiceEndpoint()
.then(logConnectionInfo)
.whenComplete(() => resumeSession()),
);
ref
.read(authProvider.notifier)
.setOpenApiServiceEndpoint()
.then(logConnectionInfo)
.whenComplete(() => resumeSession());
}
void logConnectionInfo(String? endpoint) {
@@ -60,32 +50,56 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
if (accessToken != null && serverUrl != null && endpoint != null) {
final infoProvider = ref.read(serverInfoProvider.notifier);
final wsProvider = ref.read(websocketProvider.notifier);
unawaited(
ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then(
(a) {
try {
wsProvider.connect();
infoProvider.getServerInfo();
} catch (e) {
log.severe('Failed establishing connection to the server: $e');
final backgroundManager = ref.read(backgroundSyncProvider);
final backupProvider = ref.read(driftBackupProvider.notifier);
ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then(
(_) async {
try {
wsProvider.connect();
infoProvider.getServerInfo();
if (Store.isBetaTimelineEnabled) {
await Future.wait([backgroundManager.syncLocal(), backgroundManager.syncRemote()]);
await Future.wait([
backgroundManager.hashAssets().then((_) {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
]);
if (Store.get(StoreKey.syncAlbums, false)) {
await backgroundManager.syncLinkedAlbum();
}
}
},
onError: (exception) => {
log.severe('Failed to update auth info with access token: $accessToken'),
ref.read(authProvider.notifier).logout(),
context.replaceRoute(const LoginRoute()),
},
),
} catch (e) {
log.severe('Failed establishing connection to the server: $e');
}
},
onError: (exception) => {
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');
unawaited(ref.read(authProvider.notifier).logout());
unawaited(context.replaceRoute(const LoginRoute()));
ref.read(authProvider.notifier).logout();
context.replaceRoute(const LoginRoute());
return;
}
// clean install - change the default of the flag
// current install not using beta timeline
if (context.router.current.name == SplashScreenRoute.name) {
unawaited(context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()));
final needBetaMigration = Store.get(StoreKey.needBetaMigration, false);
if (needBetaMigration) {
await Store.put(StoreKey.needBetaMigration, false);
context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]);
return;
}
context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute());
}
if (Store.isBetaTimelineEnabled) {
@@ -95,7 +109,18 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission;
if (hasPermission) {
// Resume backup (if enable) then navigate
await ref.watch(backupProvider.notifier).resumeBackup();
ref.watch(backupProvider.notifier).resumeBackup();
}
}
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
final isEnableBackup = Store.get(StoreKey.enableBackup, false);
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser != null) {
notifier.handleBackupResume(currentUser.id);
}
}
}
@@ -7,8 +7,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_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/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
@@ -17,11 +15,7 @@ import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.da
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';
import 'package:immich_mobile/utils/migration.dart';
@RoutePage()
class TabShellPage extends ConsumerStatefulWidget {
@@ -32,28 +26,6 @@ class TabShellPage extends ConsumerStatefulWidget {
}
class _TabShellPageState extends ConsumerState<TabShellPage> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
ref.read(websocketProvider.notifier).connect();
final isEnableBackup = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
await runNewSync(ref, full: true).then((_) async {
if (isEnableBackup) {
final currentUser = ref.read(currentUserProvider);
if (currentUser == null) {
return;
}
await ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
}
});
});
}
@override
Widget build(BuildContext context) {
final isScreenLandscape = context.orientation == Orientation.landscape;
@@ -134,7 +134,7 @@ class _SharedToPartnerList extends ConsumerWidget {
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text("Error loading partners: $error")),
error: (error, stack) => Center(child: Text('error_loading_partners'.tr(args: [error.toString()]))),
);
}
}
@@ -85,7 +85,7 @@ class PlacesCollectionPage extends HookConsumerWidget {
},
);
},
error: (error, stask) => const Text('Error getting places'),
error: (error, stask) => Text('error_getting_places'.tr()),
loading: () => const Center(child: CircularProgressIndicator()),
),
],
@@ -112,7 +112,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
return SwitchListTile.adaptive(
value: showMetadata.value,
onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null,
activeColor: colorScheme.primary,
activeThumbColor: colorScheme.primary,
dense: true,
title: Text("show_metadata", style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(),
);
@@ -122,7 +122,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
return SwitchListTile.adaptive(
value: allowDownload.value,
onChanged: newShareLink.value.isEmpty ? (value) => allowDownload.value = value : null,
activeColor: colorScheme.primary,
activeThumbColor: colorScheme.primary,
dense: true,
title: Text(
"allow_public_user_to_download",
@@ -135,7 +135,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
return SwitchListTile.adaptive(
value: allowUpload.value,
onChanged: newShareLink.value.isEmpty ? (value) => allowUpload.value = value : null,
activeColor: colorScheme.primary,
activeThumbColor: colorScheme.primary,
dense: true,
title: Text(
"allow_public_user_to_upload",
@@ -148,7 +148,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
return SwitchListTile.adaptive(
value: editExpiry.value,
onChanged: newShareLink.value.isEmpty ? (value) => editExpiry.value = value : null,
activeColor: colorScheme.primary,
activeThumbColor: colorScheme.primary,
dense: true,
title: Text(
"change_expiration_time",
+1 -1
View File
@@ -435,7 +435,7 @@ class SearchPage extends HookConsumerWidget {
}
},
icon: const Icon(Icons.more_vert_rounded),
tooltip: 'Show text search menu',
tooltip: 'show_text_search_menu'.tr(),
);
},
menuChildren: [
@@ -1,25 +1,25 @@
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';
import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart';
@RoutePage()
class BetaSyncSettingsPage extends StatelessWidget {
const BetaSyncSettingsPage({super.key});
class SyncStatusPage extends StatelessWidget {
const SyncStatusPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: const Text("beta_sync").t(context: context),
title: const Text("sync_status").t(context: context),
leading: IconButton(
onPressed: () => context.maybePop(true),
splashRadius: 24,
icon: const Icon(Icons.arrow_back_ios_rounded),
),
),
body: const BetaSyncSettings(),
body: const SyncStatusAndActions(),
);
}
}