chore(mobile): remove Drift* prefixes from classes and variables (#31038)

* chore(mobile): remove Drift* prefixes from classes and variables

* Remove *Drift* from related mocks
This commit is contained in:
Adam Gastineau
2026-08-26 19:11:12 +00:00
committed by GitHub
parent 093f5c070a
commit c04c81a979
92 changed files with 775 additions and 904 deletions
+4 -4
View File
@@ -30,11 +30,11 @@ abstract class MemoryData with _$MemoryData {
factory MemoryData.fromJson(String source) => MemoryData.fromMap(json.decode(source) as Map<String, dynamic>);
}
// Model for a memory stored in the server
/// A specialized collection of assets with some novel display mechanism
// TODO(agg23): DriftMemoryRepository currently mutates `assets`
@Freezed(makeCollectionsUnmodifiable: false)
abstract class DriftMemory with _$DriftMemory {
const factory DriftMemory({
abstract class Memory with _$Memory {
const factory Memory({
required String id,
required DateTime createdAt,
required DateTime updatedAt,
@@ -48,5 +48,5 @@ abstract class DriftMemory with _$DriftMemory {
DateTime? showAt,
DateTime? hideAt,
required List<RemoteAsset> assets,
}) = _DriftMemory;
}) = _Memory;
}
@@ -18,7 +18,7 @@ import 'package:immich_mobile/infrastructure/repositories/settings.repository.da
import 'package:immich_mobile/platform/background_worker_api.g.dart';
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.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/sync.provider.dart';
@@ -308,7 +308,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
}
if (Platform.isIOS) {
return _ref?.read(driftBackupProvider.notifier).startBackupWithURLSession(currentUser.id);
return _ref?.read(backupProvider.notifier).startBackupWithURLSession(currentUser.id);
}
return _ref
@@ -2,22 +2,23 @@ import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/infrastructure/repositories/memory.repository.dart';
import 'package:logging/logging.dart';
class DriftMemoryService {
final log = Logger("DriftMemoryService");
/// Accesses Memories; a specialized collection of assets with some novel display mechanism
class MemoryService {
final log = Logger("MemoryService");
final MemoryRepository _repository;
DriftMemoryService(this._repository);
MemoryService(this._repository);
Future<List<DriftMemory>> getMemoryLane(String ownerId) {
Future<List<Memory>> getMemoryLane(String ownerId) {
return _repository.getAll(ownerId);
}
Future<List<DriftMemory>> getAll(String ownerId, {bool onlyFavorites = false}) {
Future<List<Memory>> getAll(String ownerId, {bool onlyFavorites = false}) {
return _repository.getAll(ownerId, onlyToday: false, onlyFavorites: onlyFavorites);
}
Future<DriftMemory?> get(String memoryId) {
Future<Memory?> get(String memoryId) {
return _repository.get(memoryId);
}
@@ -4,11 +4,12 @@ import 'package:immich_mobile/data/db/main/dao/person.dart';
import 'package:immich_mobile/data/server/person.dart';
import 'package:immich_mobile/domain/models/person.model.dart';
class DriftPeopleService {
/// Accesses People; entities mapped to assets for presence and face detection
class PeopleService {
final PeopleRepository _repository;
final PersonApiRepository _personApiRepository;
const DriftPeopleService(this._repository, this._personApiRepository);
const PeopleService(this._repository, this._personApiRepository);
Future<Person?> get(String personId) {
return _repository.get(personId);
@@ -8,7 +8,7 @@ import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:logging/logging.dart';
@@ -26,7 +26,7 @@ class RemoteAlbumService {
static final _logger = Logger('RemoteAlbumService');
final RemoteAlbumRepository _repository;
final DriftAlbumApiRepository _albumApiRepository;
final AlbumApiRepository _albumApiRepository;
final ForegroundUploadService _uploadService;
const RemoteAlbumService(this._repository, this._albumApiRepository, this._uploadService);
@@ -8,7 +8,7 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
@@ -17,7 +17,7 @@ final syncLinkedAlbumServiceProvider = Provider((ref) {
return SyncLinkedAlbumService(
db.localAlbumRepository,
db.remoteAlbumRepository,
ref.watch(driftAlbumApiRepositoryProvider),
ref.watch(albumApiRepositoryProvider),
ref.watch(storeServiceProvider),
);
});
@@ -25,7 +25,7 @@ final syncLinkedAlbumServiceProvider = Provider((ref) {
class SyncLinkedAlbumService {
final LocalAlbumRepository _localAlbumRepository;
final RemoteAlbumRepository _remoteAlbumRepository;
final DriftAlbumApiRepository _albumApiRepository;
final AlbumApiRepository _albumApiRepository;
final StoreService _storeService;
SyncLinkedAlbumService(
@@ -12,7 +12,7 @@ class MemoryRepository extends DatabaseAccessor<Drift> with $MemoryRepositoryMix
Drift get _db => attachedDatabase;
Future<List<DriftMemory>> getAll(String ownerId, {bool onlyToday = true, bool onlyFavorites = false}) async {
Future<List<Memory>> getAll(String ownerId, {bool onlyToday = true, bool onlyFavorites = false}) async {
final query =
_db.select(_db.memoryEntity).join([
innerJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
@@ -45,7 +45,7 @@ class MemoryRepository extends DatabaseAccessor<Drift> with $MemoryRepositoryMix
return const [];
}
final Map<String, DriftMemory> memoriesMap = {};
final Map<String, Memory> memoriesMap = {};
for (final row in rows) {
final memory = row.readTable(_db.memoryEntity);
@@ -63,7 +63,7 @@ class MemoryRepository extends DatabaseAccessor<Drift> with $MemoryRepositoryMix
return memoriesMap.values.toList(growable: false);
}
Future<DriftMemory?> get(String memoryId) async {
Future<Memory?> get(String memoryId) async {
final query =
_db.select(_db.memoryEntity).join([
leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
@@ -101,8 +101,8 @@ class MemoryRepository extends DatabaseAccessor<Drift> with $MemoryRepositoryMix
}
extension on MemoryEntityData {
DriftMemory toDto() {
return DriftMemory(
Memory toDto() {
return Memory(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
@@ -12,8 +12,8 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.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/backup_album.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/permission.provider.dart';
import 'package:immich_mobile/providers/sync_status.provider.dart';
@@ -27,14 +27,14 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
@RoutePage()
class DriftBackupPage extends ConsumerStatefulWidget {
const DriftBackupPage({super.key});
class BackupPage extends ConsumerStatefulWidget {
const BackupPage({super.key});
@override
ConsumerState<DriftBackupPage> createState() => _DriftBackupPageState();
ConsumerState<BackupPage> createState() => _BackupPageState();
}
class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
class _BackupPageState extends ConsumerState<BackupPage> {
bool? syncSuccess;
@override
@@ -49,7 +49,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
}
WidgetsBinding.instance.addPostFrameCallback((_) async {
final backupNotifier = ref.read(driftBackupProvider.notifier);
final backupNotifier = ref.read(backupProvider.notifier);
final syncManager = ref.read(backgroundSyncProvider);
await backupNotifier.getBackupStatus(currentUser.id);
@@ -77,9 +77,9 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
.where((album) => album.backupSelection == BackupSelection.selected)
.toList();
final error = ref.watch(driftBackupProvider.select((p) => p.error));
final error = ref.watch(backupProvider.select((p) => p.error));
final backupNotifier = ref.watch(driftBackupProvider.notifier);
final backupNotifier = ref.watch(backupProvider.notifier);
final backupSyncManager = ref.watch(backgroundSyncProvider);
Future<void> startBackup() async {
@@ -97,7 +97,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
await backupNotifier.getBackupStatus(currentUser.id);
if (syncSuccess == false) {
Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup");
Logger("BackupPage").warning("Remote sync did not complete successfully, skipping backup");
return;
}
await backupNotifier.startForegroundBackup(currentUser.id);
@@ -117,7 +117,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
actions: [
IconButton(
onPressed: () {
unawaited(context.pushRoute(const DriftBackupOptionsRoute()));
unawaited(context.pushRoute(const BackupOptionsRoute()));
},
icon: const Icon(Icons.settings_outlined),
tooltip: context.t.backup_options,
@@ -303,7 +303,7 @@ class _BackupFooterState extends ConsumerState<_BackupFooter> with WidgetsBindin
],
TextButton.icon(
icon: const Icon(Icons.info_outline_rounded),
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
onPressed: () => context.pushRoute(const UploadDetailRoute()),
label: Text(context.t.view_details),
),
],
@@ -396,7 +396,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
),
trailing: ElevatedButton(
onPressed: () async {
await context.pushRoute(const DriftBackupAlbumSelectionRoute());
await context.pushRoute(const BackupAlbumSelectionRoute());
if (!context.mounted) {
return;
}
@@ -405,7 +405,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
if (currentUser == null) {
return;
}
unawaited(ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id));
unawaited(ref.read(backupProvider.notifier).getBackupStatus(currentUser.id));
},
child: Text(context.t.select, style: const TextStyle(fontWeight: FontWeight.bold)),
),
@@ -419,7 +419,7 @@ class _TotalCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final totalCount = ref.watch(driftBackupProvider.select((p) => p.totalCount));
final totalCount = ref.watch(backupProvider.select((p) => p.totalCount));
return BackupInfoCard(
title: context.t.total,
@@ -434,7 +434,7 @@ class _BackupCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final backupCount = ref.watch(driftBackupProvider.select((p) => p.backupCount));
final backupCount = ref.watch(backupProvider.select((p) => p.backupCount));
final syncStatus = ref.watch(syncStatusProvider);
return BackupInfoCard(
@@ -451,7 +451,7 @@ class _RemainderCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount));
final remainderCount = ref.watch(backupProvider.select((p) => p.remainderCount));
final syncStatus = ref.watch(syncStatusProvider);
return Card(
@@ -522,7 +522,7 @@ class _RemainderCard extends ConsumerWidget {
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
),
onTap: () => context.pushRoute(const DriftBackupAssetDetailRoute()),
onTap: () => context.pushRoute(const BackupAssetDetailRoute()),
title: Text(
context.t.view_details,
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)),
@@ -559,7 +559,7 @@ class _PreparingStatusState extends ConsumerState {
_pollingTimer = Timer.periodic(const Duration(seconds: 3), (timer) async {
final currentUser = ref.read(currentUserProvider);
if (currentUser != null && mounted) {
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
await ref.read(backupProvider.notifier).getBackupStatus(currentUser.id);
if (!context.mounted) {
timer.cancel();
_pollingTimer = null;
@@ -567,7 +567,7 @@ class _PreparingStatusState extends ConsumerState {
}
// Stop polling if processing count reaches 0
final updatedProcessingCount = ref.read(driftBackupProvider.select((p) => p.processingCount));
final updatedProcessingCount = ref.read(backupProvider.select((p) => p.processingCount));
if (updatedProcessingCount == 0) {
timer.cancel();
_pollingTimer = null;
@@ -582,11 +582,11 @@ class _PreparingStatusState extends ConsumerState {
@override
Widget build(BuildContext context) {
final syncStatus = ref.watch(syncStatusProvider);
final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount));
final processingCount = ref.watch(driftBackupProvider.select((p) => p.processingCount));
final remainderCount = ref.watch(backupProvider.select((p) => p.remainderCount));
final processingCount = ref.watch(backupProvider.select((p) => p.processingCount));
final readyForUploadCount = remainderCount - processingCount;
ref.listen<int>(driftBackupProvider.select((p) => p.processingCount), (previous, next) {
ref.listen<int>(backupProvider.select((p) => p.processingCount), (previous, next) {
if (next > 0 && _pollingTimer == null) {
_startPollingIfNeeded();
} else if (next == 0 && _pollingTimer != null) {
@@ -9,12 +9,12 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.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/backup_album.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
import 'package:immich_mobile/widgets/backup/album_info_list_tile.dart';
import 'package:immich_mobile/widgets/common/search_field.dart';
import 'package:logging/logging.dart';
@@ -24,14 +24,14 @@ final backupAlbumCountProvider = FutureProvider.autoDispose<int>((ref) async {
});
@RoutePage()
class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
const DriftBackupAlbumSelectionPage({super.key});
class BackupAlbumSelectionPage extends ConsumerStatefulWidget {
const BackupAlbumSelectionPage({super.key});
@override
ConsumerState<DriftBackupAlbumSelectionPage> createState() => _DriftBackupAlbumSelectionPageState();
ConsumerState<BackupAlbumSelectionPage> createState() => _BackupAlbumSelectionPageState();
}
class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbumSelectionPage> {
class _BackupAlbumSelectionPageState extends ConsumerState<BackupAlbumSelectionPage> {
String _searchQuery = '';
bool _isSearchMode = false;
int _initialTotalAssetCount = 0;
@@ -49,7 +49,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
_enableSyncUploadAlbum.value = ref.read(appConfigProvider).backup.syncAlbums;
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
_initialTotalAssetCount = ref.read(backupProvider.select((p) => p.totalCount));
}
Future<void> _handlePagePopped() async {
@@ -111,14 +111,14 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
}
final isBackupEnabled = SettingsRepository.instance.appConfig.backup.enabled;
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
await ref.read(backupProvider.notifier).getBackupStatus(user.id);
if (!mounted) {
return;
}
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
final currentTotalAssetCount = ref.read(backupProvider.select((p) => p.totalCount));
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
final backupNotifier = ref.read(driftBackupProvider.notifier);
final backupNotifier = ref.read(backupProvider.notifier);
final backgroundSync = ref.read(backgroundSyncProvider);
final nativeSync = ref.read(nativeSyncApiProvider);
if (totalChanged) {
@@ -131,7 +131,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
if (success) {
return backupNotifier.startForegroundBackup(user.id);
} else {
Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup');
Logger('BackupAlbumSelectionPage').warning('Background sync failed, not starting backup');
}
}),
);
@@ -328,7 +328,7 @@ class _AlbumSelectionList extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 12.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return DriftAlbumInfoListTile(album: filteredAlbums[index]);
return AlbumInfoListTile(album: filteredAlbums[index]);
}, childCount: filteredAlbums.length),
),
);
@@ -352,7 +352,7 @@ class _AlbumSelectionGrid extends StatelessWidget {
),
itemCount: filteredAlbums.length,
itemBuilder: (context, index) {
return DriftAlbumInfoListTile(album: filteredAlbums[index]);
return AlbumInfoListTile(album: filteredAlbums[index]);
},
),
);
@@ -9,17 +9,17 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.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/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/repositories/asset_media.repository.dart';
import 'package:immich_mobile/routing/router.dart';
@RoutePage()
class DriftBackupAssetDetailPage extends ConsumerWidget {
const DriftBackupAssetDetailPage({super.key});
class BackupAssetDetailPage extends ConsumerWidget {
const BackupAssetDetailPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<List<LocalAsset>> result = ref.watch(driftBackupCandidateProvider);
final AsyncValue<List<LocalAsset>> result = ref.watch(backupCandidateProvider);
return Scaffold(
appBar: AppBar(title: Text(context.t.backup_controller_page_remainder)),
body: result.when(
@@ -30,7 +30,7 @@ class DriftBackupAssetDetailPage extends ConsumerWidget {
itemCount: candidates.length,
itemBuilder: (context, index) {
final asset = candidates[index];
final albumsAsyncValue = ref.watch(driftCandidateBackupAlbumInfoProvider(asset.id));
final albumsAsyncValue = ref.watch(candidateBackupAlbumInfoProvider(asset.id));
final assetMediaRepository = ref.watch(assetMediaRepositoryProvider);
return FutureBuilder<String?>(
future: assetMediaRepository.getOriginalFilename(asset.id),
@@ -6,15 +6,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart';
import 'package:logging/logging.dart';
@RoutePage()
class DriftBackupOptionsPage extends ConsumerWidget {
const DriftBackupOptionsPage({super.key});
class BackupOptionsPage extends ConsumerWidget {
const BackupOptionsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -44,7 +44,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
return;
}
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
await ref.read(backupProvider.notifier).getBackupStatus(currentUser.id);
final isBackupEnabled = SettingsRepository.instance.appConfig.backup.enabled;
if (!isBackupEnabled || !context.mounted) {
return;
@@ -54,7 +54,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
SnackBar(content: Text(context.t.network_requirements_updated), duration: const Duration(seconds: 4)),
);
final backupNotifier = ref.read(driftBackupProvider.notifier);
final backupNotifier = ref.read(backupProvider.notifier);
final backgroundSync = ref.read(backgroundSyncProvider);
backupNotifier.stopForegroundBackup(reason: "backup settings updated");
unawaited(
@@ -62,7 +62,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
if (success) {
return backupNotifier.startForegroundBackup(currentUser.id);
} else {
Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup');
Logger('BackupOptionsPage').warning('Background sync failed, not starting backup');
}
}),
);
@@ -70,7 +70,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
},
child: Scaffold(
appBar: AppBar(title: Text(context.t.backup_options)),
body: const DriftBackupSettings(),
body: const BackupSettings(),
),
);
}
@@ -5,20 +5,20 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:path/path.dart' as path;
@RoutePage()
class DriftUploadDetailPage extends ConsumerStatefulWidget {
const DriftUploadDetailPage({super.key});
class UploadDetailPage extends ConsumerStatefulWidget {
const UploadDetailPage({super.key});
@override
ConsumerState<DriftUploadDetailPage> createState() => _DriftUploadDetailPageState();
ConsumerState<UploadDetailPage> createState() => _UploadDetailPageState();
}
class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
class _UploadDetailPageState extends ConsumerState<UploadDetailPage> {
final Set<String> _seenTaskIds = {};
final Set<String> _failedTaskIds = {};
@@ -26,8 +26,8 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
static const int _maxSlots = 3;
/// Assigns uploading items to fixed slots to prevent jumping when items complete
List<DriftUploadStatus?> _assignItemsToSlots(List<DriftUploadStatus> uploadingItems) {
final slots = List<DriftUploadStatus?>.filled(_maxSlots, null);
List<UploadStatus?> _assignItemsToSlots(List<UploadStatus> uploadingItems) {
final slots = List<UploadStatus?>.filled(_maxSlots, null);
final currentTaskIds = uploadingItems.map((e) => e.taskId).toSet();
_taskSlotAssignments.removeWhere((taskId, _) => !currentTaskIds.contains(taskId));
@@ -58,8 +58,8 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
@override
Widget build(BuildContext context) {
final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems));
final iCloudProgress = ref.watch(driftBackupProvider.select((state) => state.iCloudDownloadProgress));
final uploadItems = ref.watch(backupProvider.select((state) => state.uploadItems));
final iCloudProgress = ref.watch(backupProvider.select((state) => state.iCloudDownloadProgress));
for (final item in uploadItems.values) {
if (item.isFailed == true) {
@@ -91,8 +91,8 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
Widget _buildTwoSectionLayout(
BuildContext context,
List<DriftUploadStatus> uploadingItems,
List<DriftUploadStatus> failedItems,
List<UploadStatus> uploadingItems,
List<UploadStatus> failedItems,
Map<String, double> iCloudProgress,
) {
return CustomScrollView(
@@ -276,7 +276,7 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
);
}
Widget _buildCurrentUploadCard(BuildContext context, DriftUploadStatus item) {
Widget _buildCurrentUploadCard(BuildContext context, UploadStatus item) {
final double progressPercentage = (item.progress * 100).clamp(0, 100);
final isFailed = item.isFailed == true;
@@ -365,7 +365,7 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
);
}
Widget _buildErrorCard(BuildContext context, DriftUploadStatus item) {
Widget _buildErrorCard(BuildContext context, UploadStatus item) {
return Card(
elevation: 0,
color: context.colorScheme.errorContainer,
@@ -493,7 +493,7 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
);
}
Future<void> _showFileDetailDialog(BuildContext context, DriftUploadStatus item) {
Future<void> _showFileDetailDialog(BuildContext context, UploadStatus item) {
return showDialog(
context: context,
builder: (context) => FileDetailDialog(uploadStatus: item),
@@ -538,7 +538,7 @@ class _CurrentUploadThumbnail extends ConsumerWidget {
}
class FileDetailDialog extends ConsumerWidget {
final DriftUploadStatus uploadStatus;
final UploadStatus uploadStatus;
const FileDetailDialog({super.key, required this.uploadStatus});
@override
+2 -2
View File
@@ -7,7 +7,7 @@ import 'package:immich_mobile/routing/router.dart';
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/drift_backup_settings.dart';
import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart';
import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart';
import 'package:immich_mobile/widgets/settings/free_up_space_settings.dart';
import 'package:immich_mobile/widgets/settings/language_settings.dart';
@@ -59,7 +59,7 @@ enum SettingSection {
Widget get widget => switch (this) {
SettingSection.advanced => const AdvancedSettings(),
SettingSection.assetViewer => const AssetViewerSettings(),
SettingSection.backup => const DriftBackupSettings(),
SettingSection.backup => const BackupSettings(),
SettingSection.freeUpSpace => const FreeUpSpaceSettings(),
SettingSection.languages => const LanguageSettings(),
SettingSection.networking => const NetworkingSettings(),
@@ -15,7 +15,7 @@ import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/view_intent/view_intent_handler.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
@@ -310,7 +310,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final infoProvider = ref.read(serverInfoProvider.notifier);
final wsProvider = ref.read(websocketProvider.notifier);
final backgroundManager = ref.read(backgroundSyncProvider);
final backupProvider = ref.read(driftBackupProvider.notifier);
final backupNotifier = ref.read(backupProvider.notifier);
final viewIntentHandler = ref.read(viewIntentHandlerProvider);
unawaited(
@@ -334,9 +334,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
if (syncSuccess) {
await Future.wait([
backgroundManager.hashAssets().then((_) {
unawaited(_resumeBackup(backupProvider));
unawaited(_resumeBackup(backupNotifier));
}),
_resumeBackup(backupProvider),
_resumeBackup(backupNotifier),
// TODO: Bring back when the soft freeze issue is addressed
// backgroundManager.syncCloudIds(),
]);
@@ -376,7 +376,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
}
}
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
Future<void> _resumeBackup(BackupNotifier notifier) async {
final isEnableBackup = SettingsRepository.instance.appConfig.backup.enabled;
if (isEnableBackup) {
+2 -2
View File
@@ -77,7 +77,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
}
return AutoTabsRouter(
routes: const [MainTimelineRoute(), DriftSearchRoute(), DriftAlbumsRoute(), DriftLibraryRoute()],
routes: const [MainTimelineRoute(), SearchRoute(), AlbumsRoute(), LibraryRoute()],
duration: const Duration(milliseconds: 600),
transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child),
builder: (context, child) {
@@ -111,7 +111,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) {
}
if (index == kPhotoTabIndex) {
ref.invalidate(driftMemoryLaneProvider);
ref.invalidate(memoryLaneProvider);
}
if (router.activeIndex != kSearchTabIndex && index == kSearchTabIndex) {
@@ -37,7 +37,7 @@ class PinAuthPage extends HookConsumerWidget {
),
);
unawaited(context.replaceRoute(const DriftLockedFolderRoute()));
unawaited(context.replaceRoute(const LockedFolderRoute()));
}
Future<void> enableBiometricAuth() {
@@ -85,7 +85,7 @@ class PinAuthPage extends HookConsumerWidget {
child: PinVerificationForm(
autoFocus: true,
onSuccess: (_) {
unawaited(context.replaceRoute(const DriftLockedFolderRoute()));
unawaited(context.replaceRoute(const LockedFolderRoute()));
},
),
),
@@ -58,7 +58,7 @@ class EditAssetAction extends AssetActionBuilder {
ref.read(editorStateProvider.notifier).init(edits, exif);
unawaited(
context.pushRoute(
DriftEditImageRoute(
EditImageRoute(
image: Image(image: getFullImageProvider(asset, edited: false)),
applyEdits: (newEdits) => applyEdits(ref, asset.id, newEdits),
),
@@ -37,6 +37,6 @@ class SimilarPhotosAction extends ActionBuilder {
),
);
unawaited(context.navigateTo(const DriftSearchRoute()));
unawaited(context.navigateTo(const SearchRoute()));
}
}
@@ -15,7 +15,6 @@ class SlideshowAction extends ActionBuilder {
ActionItem create(BuildContext context, WidgetRef ref) => .new(
icon: Icons.slideshow,
label: context.t.slideshow,
onAction: () async =>
unawaited(context.pushRoute(DriftSlideshowRoute(timeline: ref.read(timelineServiceProvider)))),
onAction: () async => unawaited(context.pushRoute(SlideshowRoute(timeline: ref.read(timelineServiceProvider)))),
);
}
@@ -8,18 +8,18 @@ import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/like_activity_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/album/drift_activity_text_field.dart';
import 'package:immich_mobile/presentation/widgets/album/activity_text_field.dart';
import 'package:immich_mobile/providers/activity.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/widgets/activities/comment_bubble.dart';
@RoutePage()
class DriftActivitiesPage extends HookConsumerWidget {
class ActivitiesPage extends HookConsumerWidget {
final RemoteAlbum album;
final String? assetId;
final String? assetName;
const DriftActivitiesPage({super.key, required this.album, this.assetId, this.assetName});
const ActivitiesPage({super.key, required this.album, this.assetId, this.assetName});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -82,7 +82,7 @@ class DriftActivitiesPage extends HookConsumerWidget {
color: context.scaffoldBackgroundColor,
border: Border(top: BorderSide(color: context.colorScheme.secondaryContainer, width: 1)),
),
child: DriftActivityTextField(isEnabled: album.isActivityEnabled, onSubmit: onAddComment),
child: ActivityTextField(isEnabled: album.isActivityEnabled, onSubmit: onAddComment),
),
),
],
@@ -10,14 +10,14 @@ import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
@RoutePage()
class DriftAlbumsPage extends ConsumerStatefulWidget {
const DriftAlbumsPage({super.key});
class AlbumsPage extends ConsumerStatefulWidget {
const AlbumsPage({super.key});
@override
ConsumerState<DriftAlbumsPage> createState() => _DriftAlbumsPageState();
ConsumerState<AlbumsPage> createState() => _AlbumsPageState();
}
class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
class _AlbumsPageState extends ConsumerState<AlbumsPage> {
final ScrollController _scrollController = ScrollController();
Future<void> onRefresh() async {
@@ -45,7 +45,7 @@ class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
pinned: true,
actions: [
IconButton(
onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()),
onPressed: () => context.pushRoute(const CreateAlbumRoute()),
icon: const Icon(Icons.add_rounded),
),
],
@@ -11,7 +11,7 @@ import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
import 'package:immich_mobile/presentation/pages/user_selection.page.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
@@ -22,9 +22,9 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
@RoutePage()
class DriftAlbumOptionsPage extends HookConsumerWidget {
class AlbumOptionsPage extends HookConsumerWidget {
final RemoteAlbum album;
const DriftAlbumOptionsPage({super.key, required this.album});
const AlbumOptionsPage({super.key, required this.album});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -33,7 +33,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
final activityEnabled = useState(album.isActivityEnabled);
final isOwner = album.ownerId == userId;
final owner = isOwner ? ref.watch(currentUserProvider) : null;
final allUsers = isOwner ? null : ref.watch(driftUsersProvider);
final allUsers = isOwner ? null : ref.watch(usersProvider);
void showErrorMessage() {
ContextHelper(context).pop();
@@ -52,7 +52,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
return;
}
unawaited(context.navigateTo(const DriftAlbumsRoute()));
unawaited(context.navigateTo(const AlbumsRoute()));
} catch (_) {
showErrorMessage();
}
@@ -70,7 +70,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
}
Future<void> addUsers() async {
final newUsers = await context.pushRoute<List<String>>(DriftUserSelectionRoute(album: album));
final newUsers = await context.pushRoute<List<String>>(UserSelectionRoute(album: album));
if (newUsers == null || newUsers.isEmpty) {
return;
@@ -9,8 +9,8 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftArchivePage extends StatelessWidget {
const DriftArchivePage({super.key});
class ArchivePage extends StatelessWidget {
const ArchivePage({super.key});
@override
Widget build(BuildContext context) {
@@ -7,9 +7,9 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@RoutePage()
class DriftAssetSelectionTimelinePage extends StatelessWidget {
class AssetSelectionTimelinePage extends StatelessWidget {
final Set<BaseAsset> lockedSelectionAssets;
const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}});
const AssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}});
@override
Widget build(BuildContext context) {
@@ -13,14 +13,14 @@ import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@RoutePage()
class DriftCreateAlbumPage extends ConsumerStatefulWidget {
const DriftCreateAlbumPage({super.key});
class CreateAlbumPage extends ConsumerStatefulWidget {
const CreateAlbumPage({super.key});
@override
ConsumerState<DriftCreateAlbumPage> createState() => _DriftCreateAlbumPageState();
ConsumerState<CreateAlbumPage> createState() => _CreateAlbumPageState();
}
class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
class _CreateAlbumPageState extends ConsumerState<CreateAlbumPage> {
TextEditingController albumTitleController = TextEditingController();
TextEditingController albumDescriptionController = TextEditingController();
FocusNode albumTitleTextFieldFocusNode = FocusNode();
@@ -153,7 +153,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
Future<void> onSelectPhotos() async {
final assets = await context.pushRoute<Set<BaseAsset>>(
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets),
AssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets),
);
if (assets == null || assets.isEmpty) {
@@ -39,9 +39,9 @@ class _MainTimelinePageState extends ConsumerState<MainTimelinePage> {
@override
Widget build(BuildContext context) {
final hasMemories = ref.watch(driftMemoryLaneProvider.select((state) => state.value?.isNotEmpty ?? false));
final hasMemories = ref.watch(memoryLaneProvider.select((state) => state.value?.isNotEmpty ?? false));
return Timeline(
topSliverWidget: const SliverToBoxAdapter(child: DriftMemoryLane()),
topSliverWidget: const SliverToBoxAdapter(child: MemoryLane()),
topSliverWidgetHeight: hasMemories ? 200 : 0,
showStorageIndicator: true,
);
@@ -19,17 +19,17 @@ import 'package:immich_ui/immich_ui.dart';
import 'package:openapi/api.dart' show MirrorAxis, MirrorParameters, RotateParameters;
@RoutePage()
class DriftEditImagePage extends ConsumerStatefulWidget {
class EditImagePage extends ConsumerStatefulWidget {
final Image image;
final Future<void> Function(List<AssetEdit> edits) applyEdits;
const DriftEditImagePage({super.key, required this.image, required this.applyEdits});
const EditImagePage({super.key, required this.image, required this.applyEdits});
@override
ConsumerState<DriftEditImagePage> createState() => _DriftEditImagePageState();
ConsumerState<EditImagePage> createState() => _EditImagePageState();
}
class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with TickerProviderStateMixin {
class _EditImagePageState extends ConsumerState<EditImagePage> with TickerProviderStateMixin {
Future<void> _saveEditedImage() async {
ref.read(editorStateProvider.notifier).setIsEditing(true);
@@ -9,8 +9,8 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftFavoritePage extends StatelessWidget {
const DriftFavoritePage({super.key});
class FavoritePage extends StatelessWidget {
const FavoritePage({super.key});
@override
Widget build(BuildContext context) {
@@ -22,8 +22,8 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
@RoutePage()
class DriftLibraryPage extends StatelessWidget {
const DriftLibraryPage({super.key});
class LibraryPage extends StatelessWidget {
const LibraryPage({super.key});
@override
Widget build(BuildContext context) {
@@ -56,13 +56,13 @@ class _ActionButtonGrid extends ConsumerWidget {
children: [
_ActionButton(
icon: Icons.favorite_outline_rounded,
onTap: () => context.pushRoute(const DriftFavoriteRoute()),
onTap: () => context.pushRoute(const FavoriteRoute()),
label: context.t.favorites,
),
const SizedBox(width: 8),
_ActionButton(
icon: Icons.archive_outlined,
onTap: () => context.pushRoute(const DriftArchiveRoute()),
onTap: () => context.pushRoute(const ArchiveRoute()),
label: context.t.archived,
),
],
@@ -79,7 +79,7 @@ class _ActionButtonGrid extends ConsumerWidget {
isTrashEnable
? _ActionButton(
icon: Icons.delete_outline_rounded,
onTap: () => context.pushRoute(const DriftTrashRoute()),
onTap: () => context.pushRoute(const TrashRoute()),
label: context.t.trash,
)
: const SizedBox.shrink(),
@@ -161,7 +161,7 @@ class _PeopleCollectionCard extends ConsumerWidget {
final size = context.width * widthFactor - 20.0;
return GestureDetector(
onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()),
onTap: () => context.pushRoute(const PeopleCollectionRoute()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -226,7 +226,7 @@ class _PlacesCollectionCard extends StatelessWidget {
final size = context.width * widthFactor - 20.0;
return GestureDetector(
onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)),
onTap: () => context.pushRoute(PlaceRoute(currentLocation: null)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -280,7 +280,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget {
final size = context.width * widthFactor - 20.0;
return GestureDetector(
onTap: () => context.pushRoute(const DriftLocalAlbumsRoute()),
onTap: () => context.pushRoute(const LocalAlbumsRoute()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -341,7 +341,7 @@ class _MemoriesCollectionCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final memories = ref.watch(driftAllMemoriesProvider(false));
final memories = ref.watch(allMemoriesProvider(false));
return LayoutBuilder(
builder: (context, constraints) {
@@ -350,7 +350,7 @@ class _MemoriesCollectionCard extends ConsumerWidget {
final size = context.width * widthFactor - 20.0;
return GestureDetector(
onTap: () => context.pushRoute(const DriftMemoryListRoute()),
onTap: () => context.pushRoute(const MemoryListRoute()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -469,7 +469,7 @@ class _QuickAccessButtonList extends ConsumerWidget {
context.t.locked_folder,
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500),
),
onTap: () => context.pushRoute(const DriftLockedFolderRoute()),
onTap: () => context.pushRoute(const LockedFolderRoute()),
),
ListTile(
leading: const Icon(Icons.group_outlined, size: 26),
@@ -516,7 +516,7 @@ class _PartnerList extends StatelessWidget {
context.t.partner_list_user_photos(user: partner.name),
style: const TextStyle(fontWeight: FontWeight.w500),
),
onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)),
onTap: () => context.pushRoute(PartnerDetailRoute(partner: partner)),
);
},
);
@@ -11,8 +11,8 @@ import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/local_album_sliver_app_bar.dart';
@RoutePage()
class DriftLocalAlbumsPage extends StatelessWidget {
const DriftLocalAlbumsPage({super.key});
class LocalAlbumsPage extends StatelessWidget {
const LocalAlbumsPage({super.key});
@override
Widget build(BuildContext context) {
@@ -13,14 +13,14 @@ import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftLockedFolderPage extends ConsumerStatefulWidget {
const DriftLockedFolderPage({super.key});
class LockedFolderPage extends ConsumerStatefulWidget {
const LockedFolderPage({super.key});
@override
ConsumerState<DriftLockedFolderPage> createState() => _DriftLockedFolderPageState();
ConsumerState<LockedFolderPage> createState() => _LockedFolderPageState();
}
class _DriftLockedFolderPageState extends ConsumerState<DriftLockedFolderPage> with WidgetsBindingObserver {
class _LockedFolderPageState extends ConsumerState<LockedFolderPage> with WidgetsBindingObserver {
bool _showOverlay = false;
@override
@@ -8,10 +8,10 @@ import 'package:immich_mobile/presentation/widgets/map/map_settings_sheet.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
@RoutePage()
class DriftMapPage extends StatelessWidget {
class MapPage extends StatelessWidget {
final LatLng? initialLocation;
const DriftMapPage({super.key, this.initialLocation});
const MapPage({super.key, this.initialLocation});
Future<void> onSettingsPressed(BuildContext context) {
return showModalBottomSheet(
@@ -19,7 +19,7 @@ class DriftMapPage extends StatelessWidget {
showDragHandle: true,
isScrollControlled: true,
context: context,
builder: (_) => const DriftMapSettingsSheet(),
builder: (_) => const MapSettingsSheet(),
);
}
@@ -29,7 +29,7 @@ class DriftMapPage extends StatelessWidget {
extendBodyBehindAppBar: true,
body: Stack(
children: [
DriftMap(initialLocation: initialLocation),
MapView(initialLocation: initialLocation),
Positioned(
left: 20,
top: 70,
@@ -20,13 +20,13 @@ import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart';
/// Expects the current asset to be set via [assetViewerProvider] before navigating to this page
@RoutePage()
class DriftMemoryPage extends HookConsumerWidget {
final List<DriftMemory> memories;
class MemoryPage extends HookConsumerWidget {
final List<Memory> memories;
final int memoryIndex;
const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key});
const MemoryPage({required this.memories, required this.memoryIndex, super.key});
static void setMemory(WidgetRef ref, DriftMemory memory) {
static void setMemory(WidgetRef ref, Memory memory) {
if (memory.assets.isNotEmpty) {
ref.read(assetViewerProvider.notifier).setAsset(memory.assets.first);
}
@@ -221,7 +221,7 @@ class DriftMemoryPage extends HookConsumerWidget {
currentMemory.value = memories[pageNumber];
WidgetsBinding.instance.addPostFrameCallback((_) {
DriftMemoryPage.setMemory(ref, memories[pageNumber]);
MemoryPage.setMemory(ref, memories[pageNumber]);
});
// Update currentAsset to the first asset of the new memory
@@ -285,7 +285,7 @@ class DriftMemoryPage extends HookConsumerWidget {
children: [
ColoredBox(
color: Colors.black,
child: DriftMemoryCard(
child: MemoryCard(
asset: asset,
title: title,
showTitle: index == 0,
@@ -348,7 +348,7 @@ class DriftMemoryPage extends HookConsumerWidget {
],
),
),
DriftMemoryBottomInfo(memory: memories[mIndex], title: title),
MemoryBottomInfo(memory: memories[mIndex], title: title),
],
);
},
@@ -6,25 +6,25 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
import 'package:immich_mobile/presentation/pages/memory.page.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/routing/router.dart';
@RoutePage()
class DriftMemoryListPage extends ConsumerStatefulWidget {
const DriftMemoryListPage({super.key});
class MemoryListPage extends ConsumerStatefulWidget {
const MemoryListPage({super.key});
@override
ConsumerState<DriftMemoryListPage> createState() => _DriftMemoryListPageState();
ConsumerState<MemoryListPage> createState() => _MemoryListPageState();
}
class _DriftMemoryListPageState extends ConsumerState<DriftMemoryListPage> {
class _MemoryListPageState extends ConsumerState<MemoryListPage> {
bool _onlyFavorites = false;
@override
Widget build(BuildContext context) {
final memories = ref.watch(driftAllMemoriesProvider(_onlyFavorites));
final memories = ref.watch(allMemoriesProvider(_onlyFavorites));
return LayoutBuilder(
builder: (context, constraints) {
@@ -54,8 +54,8 @@ class _DriftMemoryListPageState extends ConsumerState<DriftMemoryListPage> {
itemCount: memories.length,
itemBuilder: (context, index) => GestureDetector(
onTap: () {
DriftMemoryPage.setMemory(ref, memories[index]);
unawaited(context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)));
MemoryPage.setMemory(ref, memories[index]);
unawaited(context.pushRoute(MemoryRoute(memories: memories, memoryIndex: index)));
},
child: Stack(
children: [
@@ -13,10 +13,10 @@ 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 {
class PartnerDetailPage extends StatelessWidget {
final Partner partner;
const DriftPartnerDetailPage({super.key, required this.partner});
const PartnerDetailPage({super.key, required this.partner});
@override
Widget build(BuildContext context) {
@@ -15,14 +15,14 @@ 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});
class PeopleCollectionPage extends ConsumerStatefulWidget {
const PeopleCollectionPage({super.key});
@override
ConsumerState<DriftPeopleCollectionPage> createState() => _DriftPeopleCollectionPageState();
ConsumerState<PeopleCollectionPage> createState() => _PeopleCollectionPageState();
}
class _DriftPeopleCollectionPageState extends ConsumerState<DriftPeopleCollectionPage> {
class _PeopleCollectionPageState extends ConsumerState<PeopleCollectionPage> {
final FocusNode _formFocus = FocusNode();
String? _search;
@@ -92,7 +92,7 @@ class _DriftPeopleCollectionPageState extends ConsumerState<DriftPeopleCollectio
children: [
GestureDetector(
onTap: () {
unawaited(context.pushRoute(DriftPersonRoute(person: person)));
unawaited(context.pushRoute(PersonRoute(person: person)));
},
child: Material(
shape: const CircleBorder(side: BorderSide.none),
@@ -13,16 +13,16 @@ import 'package:immich_mobile/utils/people.utils.dart';
import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart';
@RoutePage()
class DriftPersonPage extends ConsumerStatefulWidget {
class PersonPage extends ConsumerStatefulWidget {
final Person person;
const DriftPersonPage({super.key, required this.person});
const PersonPage({super.key, required this.person});
@override
ConsumerState<DriftPersonPage> createState() => _DriftPersonPageState();
ConsumerState<PersonPage> createState() => _PersonPageState();
}
class _DriftPersonPageState extends ConsumerState<DriftPersonPage> {
class _PersonPageState extends ConsumerState<PersonPage> {
late Person _person;
@override
@@ -13,8 +13,8 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
@RoutePage()
class DriftPlacePage extends StatelessWidget {
const DriftPlacePage({super.key, this.currentLocation});
class PlacePage extends StatelessWidget {
const PlacePage({super.key, this.currentLocation});
final LatLng? currentLocation;
@@ -94,7 +94,7 @@ class _Map extends StatelessWidget {
height: 200,
width: context.width,
child: MapThumbnail(
onTap: (_, _) => context.pushRoute(DriftMapRoute(initialLocation: currentLocation)),
onTap: (_, _) => context.pushRoute(MapRoute(initialLocation: currentLocation)),
zoom: 8,
centre: currentLocation ?? const LatLng(21.44950, -157.91959),
showAttribution: false,
@@ -163,7 +163,7 @@ class _PlaceTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LargeLeadingTile(
onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)),
onTap: () => context.pushRoute(PlaceDetailRoute(place: place.$1)),
title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)),
leading: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
@@ -6,10 +6,10 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftPlaceDetailPage extends StatelessWidget {
class PlaceDetailPage extends StatelessWidget {
final String place;
const DriftPlaceDetailPage({super.key, required this.place});
const PlaceDetailPage({super.key, required this.place});
@override
Widget build(BuildContext context) {
@@ -11,7 +11,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup_server.provider.dart';
import 'package:immich_mobile/providers/upload_profile_image.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/utils/image_converter.dart';
@@ -89,7 +89,7 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
if (user != null) {
unawaited(ref.read(currentUserProvider.notifier).refresh());
}
unawaited(ref.read(backupProvider.notifier).updateDiskInfo());
unawaited(ref.read(backupServerProvider.notifier).updateDiskInfo());
ImmichToast.show(
context: context,
@@ -8,8 +8,8 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftRecentlyAddedPage extends StatelessWidget {
const DriftRecentlyAddedPage({super.key});
class RecentlyAddedPage extends StatelessWidget {
const RecentlyAddedPage({super.key});
@override
Widget build(BuildContext context) {
@@ -8,8 +8,8 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftRecentlyTakenPage extends StatelessWidget {
const DriftRecentlyTakenPage({super.key});
class RecentlyTakenPage extends StatelessWidget {
const RecentlyTakenPage({super.key});
@override
Widget build(BuildContext context) {
@@ -10,7 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/album/pending_uploads_banner.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart';
import 'package:immich_mobile/presentation/widgets/remote_album/drift_album_option.widget.dart';
import 'package:immich_mobile/presentation/widgets/remote_album/album_option.widget.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
@@ -47,7 +47,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
}
final newAssets = await context.pushRoute<Set<BaseAsset>>(
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()),
AssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()),
);
if (newAssets == null || newAssets.isEmpty) {
@@ -69,7 +69,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
}
Future<void> addUsers(BuildContext context) async {
final newUsers = await context.pushRoute<List<String>>(DriftUserSelectionRoute(album: _album));
final newUsers = await context.pushRoute<List<String>>(UserSelectionRoute(album: _album));
if (newUsers == null || newUsers.isEmpty) {
return;
@@ -145,7 +145,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
ImmichToast.show(context: context, msg: context.t.album_deleted, toastType: ToastType.success);
unawaited(context.pushRoute(const DriftAlbumsRoute()));
unawaited(context.pushRoute(const AlbumsRoute()));
} catch (e) {
if (!context.mounted) {
return;
@@ -180,7 +180,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
}
Future<void> showActivity(BuildContext context) async {
unawaited(context.pushRoute(DriftActivitiesRoute(album: _album)));
unawaited(context.pushRoute(ActivitiesRoute(album: _album)));
}
@override
@@ -209,7 +209,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
onToggleAlbumOrder: () => toggleAlbumOrder(),
onEditAlbum: () => showEditTitleAndDescription(context),
onCreateSharedLink: () => unawaited(context.pushRoute(SharedLinkEditRoute(albumId: _album.id))),
onShowOptions: () => context.pushRoute(DriftAlbumOptionsRoute(album: _album)),
onShowOptions: () => context.pushRoute(AlbumOptionsRoute(album: _album)),
),
onEditTitle: isOwner ? () => showEditTitleAndDescription(context) : null,
onActivity: () => showActivity(context),
@@ -428,7 +428,7 @@ class _AlbumKebabMenu extends ConsumerWidget {
builder: (context, snapshot) {
final canAddPhotos = snapshot.data ?? false;
return DriftRemoteAlbumOption(
return RemoteAlbumOption(
iconColor: iconColor,
iconShadows: iconShadows,
onDeleteAlbum: isOwner ? onDeleteAlbum : null,
@@ -37,8 +37,8 @@ import 'package:immich_mobile/widgets/search/search_filter/search_filter_utils.d
import 'package:immich_mobile/widgets/search/search_filter/star_rating_picker.dart';
@RoutePage()
class DriftSearchPage extends HookConsumerWidget {
const DriftSearchPage({super.key});
class SearchPage extends HookConsumerWidget {
const SearchPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -892,23 +892,23 @@ class _QuickLinkList extends StatelessWidget {
title: context.t.recently_taken,
icon: Icons.schedule_outlined,
isTop: true,
onTap: () => context.pushRoute(const DriftRecentlyTakenRoute()),
onTap: () => context.pushRoute(const RecentlyTakenRoute()),
),
_QuickLink(
title: context.t.recently_added,
icon: Icons.upload_outlined,
onTap: () => context.pushRoute(const DriftRecentlyAddedRoute()),
onTap: () => context.pushRoute(const RecentlyAddedRoute()),
),
_QuickLink(
title: context.t.videos,
icon: Icons.play_circle_outline_rounded,
onTap: () => context.pushRoute(const DriftVideoRoute()),
onTap: () => context.pushRoute(const VideoRoute()),
),
_QuickLink(
title: context.t.favorites,
icon: Icons.favorite_border_rounded,
isBottom: true,
onTap: () => context.pushRoute(const DriftFavoriteRoute()),
onTap: () => context.pushRoute(const FavoriteRoute()),
),
],
),
@@ -26,16 +26,16 @@ import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
@RoutePage()
class DriftSlideshowPage extends ConsumerStatefulWidget {
class SlideshowPage extends ConsumerStatefulWidget {
final TimelineService timeline;
const DriftSlideshowPage({super.key, required this.timeline});
const SlideshowPage({super.key, required this.timeline});
@override
ConsumerState<DriftSlideshowPage> createState() => _DriftSlideshowPageState();
ConsumerState<SlideshowPage> createState() => _SlideshowPageState();
}
class _DriftSlideshowPageState extends ConsumerState<DriftSlideshowPage> with SingleTickerProviderStateMixin {
class _SlideshowPageState extends ConsumerState<SlideshowPage> with SingleTickerProviderStateMixin {
static const double _kenBurnsZoom = 0.1;
late SlideshowConfig _config;
@@ -14,8 +14,8 @@ import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@RoutePage()
class DriftTrashPage extends StatelessWidget {
const DriftTrashPage({super.key});
class TrashPage extends StatelessWidget {
const TrashPage({super.key});
@override
Widget build(BuildContext context) {
@@ -15,7 +15,7 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
// TODO: Refactor this provider when we have user provider/service/repository pattern in place
final driftUsersProvider = FutureProvider.autoDispose<List<UserDto>>((ref) async {
final usersProvider = FutureProvider.autoDispose<List<UserDto>>((ref) async {
final drift = ref.watch(driftProvider);
final currentUser = ref.watch(currentUserProvider);
@@ -44,14 +44,14 @@ final driftUsersProvider = FutureProvider.autoDispose<List<UserDto>>((ref) async
});
@RoutePage()
class DriftUserSelectionPage extends HookConsumerWidget {
class UserSelectionPage extends HookConsumerWidget {
final RemoteAlbum album;
const DriftUserSelectionPage({super.key, required this.album});
const UserSelectionPage({super.key, required this.album});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<List<UserDto>> suggestedShareUsers = ref.watch(driftUsersProvider);
final AsyncValue<List<UserDto>> suggestedShareUsers = ref.watch(usersProvider);
final sharedUsers = ref.watch(remoteAlbumSharedUsersProvider(album.id));
final sharedUsersList = useState<Set<UserDto>>({});
@@ -8,8 +8,8 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class DriftVideoPage extends StatelessWidget {
const DriftVideoPage({super.key});
class VideoPage extends StatelessWidget {
const VideoPage({super.key});
@override
Widget build(BuildContext context) {
@@ -5,14 +5,14 @@ import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
class DriftActivityTextField extends ConsumerStatefulWidget {
class ActivityTextField extends ConsumerStatefulWidget {
final bool isEnabled;
final bool isBottomSheet;
final String? likeId;
final Function(String) onSubmit;
final Function()? onKeyboardFocus;
const DriftActivityTextField({
const ActivityTextField({
required this.onSubmit,
this.isEnabled = true,
this.likeId,
@@ -22,10 +22,10 @@ class DriftActivityTextField extends ConsumerStatefulWidget {
});
@override
ConsumerState<DriftActivityTextField> createState() => _DriftActivityTextFieldState();
ConsumerState<ActivityTextField> createState() => _ActivityTextFieldState();
}
class _DriftActivityTextFieldState extends ConsumerState<DriftActivityTextField> {
class _ActivityTextFieldState extends ConsumerState<ActivityTextField> {
late FocusNode inputFocusNode;
late TextEditingController inputController;
bool sendEnabled = false;
@@ -28,18 +28,18 @@ class PeopleDetails extends ConsumerWidget {
return const SizedBox.shrink();
}
final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id));
final peopleFuture = ref.watch(peopleAssetProvider(asset.id));
Future<void> showNameEditModal(Person person) async {
await showDialog(
context: context,
useRootNavigator: false,
builder: (BuildContext context) {
return DriftPersonNameEditForm(person: person);
return PersonNameEditForm(person: person);
},
);
ref.invalidate(driftPeopleAssetProvider(asset.id));
ref.invalidate(peopleAssetProvider(asset.id));
}
return peopleFuture.when(
@@ -71,12 +71,12 @@ class PeopleDetails extends ConsumerWidget {
final previousRouteArgs = previousRouteData?.arguments;
// Prevent circular navigation
if (previousRouteArgs is DriftPersonRouteArgs && previousRouteArgs.person.id == person.id) {
if (previousRouteArgs is PersonRouteArgs && previousRouteArgs.person.id == person.id) {
context.back();
return;
}
ContextHelper(context).pop();
unawaited(context.pushRoute(DriftPersonRoute(person: person)));
unawaited(context.pushRoute(PersonRoute(person: person)));
},
onNameTap: () => showNameEditModal(person),
),
@@ -55,11 +55,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
onPressed: () {
unawaited(
context.router.push(
DriftActivitiesRoute(
album: album,
assetId: asset is RemoteAsset ? asset.id : null,
assetName: asset.name,
),
ActivitiesRoute(album: album, assetId: asset is RemoteAsset ? asset.id : null, assetName: asset.name),
),
);
},
@@ -2,7 +2,7 @@ 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/generated/translations.g.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
class BackupToggleButton extends ConsumerStatefulWidget {
@@ -55,13 +55,13 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
@override
Widget build(BuildContext context) {
final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems));
final uploadTasks = ref.watch(backupProvider.select((state) => state.uploadItems));
final isSyncing = ref.watch(driftBackupProvider.select((state) => state.isSyncing));
final isSyncing = ref.watch(backupProvider.select((state) => state.isSyncing));
final iCloudProgress = ref.watch(driftBackupProvider.select((state) => state.iCloudDownloadProgress));
final iCloudProgress = ref.watch(backupProvider.select((state) => state.iCloudDownloadProgress));
final errorCount = ref.watch(driftBackupProvider.select((state) => state.errorCount));
final errorCount = ref.watch(backupProvider.select((state) => state.errorCount));
final isProcessing = uploadTasks.isNotEmpty || isSyncing || iCloudProgress.isNotEmpty;
@@ -39,16 +39,16 @@ class CustomSourceProperties implements SourceProperties {
}
}
class DriftMap extends ConsumerStatefulWidget {
class MapView extends ConsumerStatefulWidget {
final LatLng? initialLocation;
const DriftMap({super.key, this.initialLocation});
const MapView({super.key, this.initialLocation});
@override
ConsumerState<DriftMap> createState() => _DriftMapState();
ConsumerState<MapView> createState() => _MapViewState();
}
class _DriftMapState extends ConsumerState<DriftMap> {
class _MapViewState extends ConsumerState<MapView> {
MapLibreMapController? mapController;
final _reloadMutex = AsyncMutex();
final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2));
@@ -8,14 +8,14 @@ import 'package:immich_mobile/widgets/map/map_settings/map_settings_list_tile.da
import 'package:immich_mobile/widgets/map/map_settings/map_settings_time_dropdown.dart';
import 'package:immich_mobile/widgets/map/map_settings/map_theme_picker.dart';
class DriftMapSettingsSheet extends ConsumerStatefulWidget {
const DriftMapSettingsSheet({super.key});
class MapSettingsSheet extends ConsumerStatefulWidget {
const MapSettingsSheet({super.key});
@override
ConsumerState<DriftMapSettingsSheet> createState() => _DriftMapSettingsSheetState();
ConsumerState<MapSettingsSheet> createState() => _MapSettingsSheetState();
}
class _DriftMapSettingsSheetState extends ConsumerState<DriftMapSettingsSheet> {
class _MapSettingsSheetState extends ConsumerState<MapSettingsSheet> {
late bool useCustomRange;
@override
@@ -7,10 +7,10 @@ import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:intl/intl.dart';
class DriftMemoryBottomInfo extends StatelessWidget {
final DriftMemory memory;
class MemoryBottomInfo extends StatelessWidget {
final Memory memory;
final String title;
const DriftMemoryBottomInfo({super.key, required this.memory, required this.title});
const MemoryBottomInfo({super.key, required this.memory, required this.title});
@override
Widget build(BuildContext context) {
@@ -9,14 +9,14 @@ import 'package:immich_mobile/presentation/widgets/images/full_image.widget.dart
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
class DriftMemoryCard extends StatelessWidget {
class MemoryCard extends StatelessWidget {
final RemoteAsset asset;
final String title;
final bool showTitle;
final bool isCurrent;
final Function()? onVideoEnded;
const DriftMemoryCard({
const MemoryCard({
required this.asset,
required this.title,
required this.showTitle,
@@ -5,19 +5,19 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
import 'package:immich_mobile/presentation/pages/memory.page.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/routing/router.dart';
class DriftMemoryLane extends ConsumerWidget {
const DriftMemoryLane({super.key});
class MemoryLane extends ConsumerWidget {
const MemoryLane({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final memoryLaneProvider = ref.watch(driftMemoryLaneProvider);
final memories = memoryLaneProvider.value ?? const [];
final memoryLane = ref.watch(memoryLaneProvider);
final memories = memoryLane.value ?? const [];
if (memories.isEmpty) {
return const SizedBox.shrink();
}
@@ -33,22 +33,20 @@ class DriftMemoryLane extends ConsumerWidget {
onTap: (index) {
ref.read(hapticFeedbackProvider.notifier).heavyImpact();
if (memories[index].assets.isNotEmpty) {
DriftMemoryPage.setMemory(ref, memories[index]);
MemoryPage.setMemory(ref, memories[index]);
}
unawaited(context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)));
unawaited(context.pushRoute(MemoryRoute(memories: memories, memoryIndex: index)));
},
children: memories
.map((memory) => DriftMemoryCard(key: Key(memory.id), memory: memory))
.toList(growable: false),
children: memories.map((memory) => MemoryCard(key: Key(memory.id), memory: memory)).toList(growable: false),
),
);
}
}
class DriftMemoryCard extends StatelessWidget {
const DriftMemoryCard({super.key, required this.memory});
class MemoryCard extends StatelessWidget {
const MemoryCard({super.key, required this.memory});
final DriftMemory memory;
final Memory memory;
@override
Widget build(BuildContext context) {
@@ -10,16 +10,16 @@ import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:scroll_date_picker/scroll_date_picker.dart';
class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget {
class PersonBirthdayEditForm extends ConsumerStatefulWidget {
final Person person;
const DriftPersonBirthdayEditForm({super.key, required this.person});
const PersonBirthdayEditForm({super.key, required this.person});
@override
ConsumerState<DriftPersonBirthdayEditForm> createState() => _DriftPersonNameEditFormState();
ConsumerState<PersonBirthdayEditForm> createState() => _PersonNameEditFormState();
}
class _DriftPersonNameEditFormState extends ConsumerState<DriftPersonBirthdayEditForm> {
class _PersonNameEditFormState extends ConsumerState<PersonBirthdayEditForm> {
late DateTime _selectedDate;
@override
@@ -30,7 +30,7 @@ class _DriftPersonNameEditFormState extends ConsumerState<DriftPersonBirthdayEdi
Future<void> saveBirthday() async {
try {
final result = await ref.read(driftPeopleServiceProvider).updateBirthday(widget.person.id, _selectedDate);
final result = await ref.read(peopleServiceProvider).updateBirthday(widget.person.id, _selectedDate);
if (result != 0 && mounted) {
context.pop<DateTime>(_selectedDate);
@@ -8,16 +8,16 @@ import 'package:immich_mobile/providers/infrastructure/people.provider.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class DriftPersonNameEditForm extends ConsumerStatefulWidget {
class PersonNameEditForm extends ConsumerStatefulWidget {
final Person person;
const DriftPersonNameEditForm({super.key, required this.person});
const PersonNameEditForm({super.key, required this.person});
@override
ConsumerState<DriftPersonNameEditForm> createState() => _DriftPersonNameEditFormState();
ConsumerState<PersonNameEditForm> createState() => _PersonNameEditFormState();
}
class _DriftPersonNameEditFormState extends ConsumerState<DriftPersonNameEditForm> {
class _PersonNameEditFormState extends ConsumerState<PersonNameEditForm> {
late TextEditingController _formController;
@override
@@ -28,7 +28,7 @@ class _DriftPersonNameEditFormState extends ConsumerState<DriftPersonNameEditFor
Future<void> onEdit(String personId, String newName) async {
try {
final result = await ref.read(driftPeopleServiceProvider).updateName(personId, newName);
final result = await ref.read(peopleServiceProvider).updateName(personId, newName);
if (result != 0 && mounted) {
context.pop<String>(newName);
}
@@ -3,8 +3,8 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
class DriftRemoteAlbumOption extends StatelessWidget {
const DriftRemoteAlbumOption({
class RemoteAlbumOption extends StatelessWidget {
const RemoteAlbumOption({
super.key,
this.onAddPhotos,
this.onAddUsers,
@@ -7,7 +7,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
@@ -128,7 +128,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
syncSuccess = await backgroundManager.syncRemote();
}, "syncRemote"),
]);
_ref.invalidate(driftAllMemoriesProvider);
_ref.invalidate(allMemoriesProvider);
if (syncSuccess) {
await Future.wait([
_safeRun(backgroundManager.hashAssets, "hashAssets").then((_) {
@@ -157,7 +157,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser != null) {
await _safeRun(
() => _ref.read(driftBackupProvider.notifier).startForegroundBackup(currentUser.id),
() => _ref.read(backupProvider.notifier).startForegroundBackup(currentUser.id),
"handleBackupResume",
);
}
@@ -207,7 +207,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
Future<void> _performPause() {
if (_ref.read(authProvider).isAuthenticated) {
_ref.read(driftBackupProvider.notifier).stopForegroundBackup(reason: "the app being sent to the background");
_ref.read(backupProvider.notifier).stopForegroundBackup(reason: "the app being sent to the background");
_ref.read(websocketProvider.notifier).disconnect();
}
@@ -1,6 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/utils/background_sync.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/sync_status.provider.dart';
final backgroundSyncProvider = Provider<BackgroundSyncManager>((ref) {
@@ -10,17 +10,17 @@ final backgroundSyncProvider = Provider<BackgroundSyncManager>((ref) {
onRemoteSyncStart: () {
syncStatusNotifier.startRemoteSync();
// ignore: dispose-provided-instances
final backupProvider = ref.read(driftBackupProvider.notifier);
if (backupProvider.mounted) {
backupProvider.updateError(BackupError.none);
final backupNotifier = ref.read(backupProvider.notifier);
if (backupNotifier.mounted) {
backupNotifier.updateError(BackupError.none);
}
},
onRemoteSyncComplete: (isSuccess) {
syncStatusNotifier.completeRemoteSync();
// ignore: dispose-provided-instances
final backupProvider = ref.read(driftBackupProvider.notifier);
if (backupProvider.mounted) {
backupProvider.updateError(isSuccess == true ? BackupError.none : BackupError.syncFailed);
final backupNotifier = ref.read(backupProvider.notifier);
if (backupNotifier.mounted) {
backupNotifier.updateError(isSuccess == true ? BackupError.none : BackupError.syncFailed);
}
},
onRemoteSyncError: syncStatusNotifier.errorRemoteSync,
+286 -12
View File
@@ -1,23 +1,297 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/services/server_info.service.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/utils/upload_speed_calculator.dart';
import 'package:logging/logging.dart';
final backupProvider = StateNotifierProvider<BackupNotifier, ServerDiskInfo>((ref) {
return BackupNotifier(ref.watch(serverInfoServiceProvider));
part 'backup.provider.freezed.dart';
@freezed
abstract class EnqueueStatus with _$EnqueueStatus {
const factory EnqueueStatus({required int enqueueCount, required int totalCount}) = _EnqueueStatus;
}
@freezed
abstract class UploadStatus with _$UploadStatus {
const factory UploadStatus({
required String taskId,
required String filename,
required double progress,
required int fileSize,
required String networkSpeedAsString,
bool? isFailed,
String? error,
}) = _UploadStatus;
}
enum BackupError { none, syncFailed }
@freezed
abstract class BackupState with _$BackupState {
const BackupState._();
const factory BackupState({
required int totalCount,
required int backupCount,
required int remainderCount,
required int processingCount,
required bool isSyncing,
@Default(BackupError.none) BackupError error,
required Map<String, UploadStatus> uploadItems,
@Default({}) Map<String, double> iCloudDownloadProgress,
}) = _BackupState;
int get errorCount => uploadItems.values.where((item) => item.isFailed == true).length;
}
final backupProvider = StateNotifierProvider<BackupNotifier, BackupState>((ref) {
return BackupNotifier(
ref.watch(foregroundUploadServiceProvider),
ref.watch(backgroundUploadServiceProvider),
UploadSpeedManager(),
);
});
class BackupNotifier extends StateNotifier<ServerDiskInfo> {
BackupNotifier(this._serverInfoService)
: super(const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0));
class BackupNotifier extends StateNotifier<BackupState> {
BackupNotifier(this._foregroundUploadService, this._backgroundUploadService, this._uploadSpeedManager)
: super(
const BackupState(
totalCount: 0,
backupCount: 0,
remainderCount: 0,
processingCount: 0,
isSyncing: false,
uploadItems: {},
error: BackupError.none,
),
);
final ServerInfoService _serverInfoService;
final ForegroundUploadService _foregroundUploadService;
final BackgroundUploadService _backgroundUploadService;
final UploadSpeedManager _uploadSpeedManager;
Completer<void>? _cancelToken;
Future<void> updateDiskInfo() async {
final diskInfo = await _serverInfoService.getDiskInfo();
if (diskInfo != null) {
state = diskInfo;
final _logger = Logger("BackupNotifier");
/// Remove upload item from state
void _removeUploadItem(String taskId) {
if (!mounted) {
_logger.warning("Skip _removeUploadItem: notifier disposed");
return;
}
if (state.uploadItems.containsKey(taskId)) {
final updatedItems = Map<String, UploadStatus>.from(state.uploadItems);
updatedItems.remove(taskId);
state = state.copyWith(uploadItems: updatedItems);
}
}
Future<void> getBackupStatus(String userId) async {
if (!mounted) {
_logger.warning("Skip getBackupStatus (pre-call): notifier disposed");
return;
}
final counts = await _foregroundUploadService.getBackupCounts(userId);
if (!mounted) {
_logger.warning("Skip getBackupStatus (post-call): notifier disposed");
return;
}
state = state.copyWith(
totalCount: counts.total,
backupCount: counts.total - counts.remainder,
remainderCount: counts.remainder,
processingCount: counts.processing,
);
}
void updateError(BackupError error) {
if (!mounted) {
_logger.warning("Skip updateError: notifier disposed");
return;
}
state = state.copyWith(error: error);
}
void updateSyncing(bool isSyncing) {
state = state.copyWith(isSyncing: isSyncing);
}
Future<void> startForegroundBackup(String userId) async {
// Cancel any existing backup before starting a new one
if (_cancelToken != null) {
stopForegroundBackup(reason: "restarting the backup");
}
state = state.copyWith(error: BackupError.none);
// A pause during the recount below nulls _cancelToken, so the run keeps its own reference.
final cancelToken = Completer<void>();
_cancelToken = cancelToken;
// Re-baseline the counters against the same DB read that feeds this run's candidate list,
// otherwise a resume counts duplicate successes against the old baseline (#26215).
await getBackupStatus(userId);
return _foregroundUploadService.uploadCandidates(
userId,
cancelToken,
callbacks: UploadCallbacks(
onProgress: _handleForegroundBackupProgress,
onSuccess: _handleForegroundBackupSuccess,
onError: _handleForegroundBackupError,
onICloudProgress: _handleICloudProgress,
),
);
}
void stopForegroundBackup({required String reason}) {
if (_cancelToken != null) {
_logger.info("Foreground backup cancelled: $reason");
}
_cancelToken?.complete();
_cancelToken = null;
_uploadSpeedManager.clear();
state = state.copyWith(uploadItems: {}, iCloudDownloadProgress: {});
}
void _handleICloudProgress(String localAssetId, double progress) {
state = state.copyWith(iCloudDownloadProgress: {...state.iCloudDownloadProgress, localAssetId: progress});
if (progress >= 1.0) {
Future.delayed(const Duration(milliseconds: 250), () {
final updatedProgress = Map<String, double>.from(state.iCloudDownloadProgress);
updatedProgress.remove(localAssetId);
state = state.copyWith(iCloudDownloadProgress: updatedProgress);
});
}
}
void _handleForegroundBackupProgress(String localAssetId, String filename, int bytes, int totalBytes) {
if (_cancelToken == null) {
return;
}
final progress = totalBytes > 0 ? bytes / totalBytes : 0.0;
final networkSpeedAsString = _uploadSpeedManager.updateProgress(localAssetId, bytes, totalBytes);
final currentItem = state.uploadItems[localAssetId];
if (currentItem != null) {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: currentItem.copyWith(
filename: filename,
progress: progress,
fileSize: totalBytes,
networkSpeedAsString: networkSpeedAsString,
),
},
);
} else {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: UploadStatus(
taskId: localAssetId,
filename: filename,
progress: progress,
fileSize: totalBytes,
networkSpeedAsString: networkSpeedAsString,
),
},
);
}
}
void _handleForegroundBackupSuccess(String localAssetId, String remoteAssetId) {
if (!mounted) {
_logger.warning("Skip _handleForegroundBackupSuccess: notifier disposed");
return;
}
state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1);
_uploadSpeedManager.removeTask(localAssetId);
Future.delayed(const Duration(milliseconds: 1000), () {
_removeUploadItem(localAssetId);
});
}
void _handleForegroundBackupError(String localAssetId, String errorMessage) {
_logger.severe("Upload failed for $localAssetId: $errorMessage");
final currentItem = state.uploadItems[localAssetId];
if (currentItem != null) {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: currentItem.copyWith(isFailed: true, error: errorMessage),
},
);
} else {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: UploadStatus(
taskId: localAssetId,
filename: 'Unknown',
progress: 0,
fileSize: 0,
networkSpeedAsString: '',
isFailed: true,
error: errorMessage,
),
},
);
}
_uploadSpeedManager.removeTask(localAssetId);
}
Future<void> startBackupWithURLSession(String userId) async {
if (!mounted) {
_logger.warning("Skip handleBackupResume (pre-call): notifier disposed");
return;
}
_logger.info("Start background backup sequence");
state = state.copyWith(error: BackupError.none);
final tasks = await _backgroundUploadService.getActiveTasks(kBackupGroup);
if (!mounted) {
_logger.warning("Skip handleBackupResume (post-call): notifier disposed");
return;
}
_logger.info("Found ${tasks.length} pending tasks");
if (tasks.isEmpty) {
_logger.info("No pending tasks, starting new upload");
return _backgroundUploadService.uploadBackupCandidates(userId);
}
_logger.info("Resuming upload ${tasks.length} assets");
return _backgroundUploadService.resume();
}
}
final backupCandidateProvider = FutureProvider.autoDispose<List<LocalAsset>>((ref) {
final user = ref.watch(currentUserProvider);
if (user == null) {
return [];
}
return ref.read(foregroundUploadServiceProvider).getBackupCandidates(user.id, onlyHashed: false);
});
final candidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family<List<LocalAlbum>, String>((ref, assetId) {
return ref
.read(driftProvider)
.localAssetRepository
.getSourceAlbums(assetId, backupSelection: BackupSelection.selected);
});
@@ -0,0 +1,23 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/services/server_info.service.dart';
final backupServerProvider = StateNotifierProvider<BackupServerNotifier, ServerDiskInfo>((ref) {
return BackupServerNotifier(ref.watch(serverInfoServiceProvider));
});
class BackupServerNotifier extends StateNotifier<ServerDiskInfo> {
BackupServerNotifier(this._serverInfoService)
: super(const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0));
final ServerInfoService _serverInfoService;
Future<void> updateDiskInfo() async {
final diskInfo = await _serverInfoService.getDiskInfo();
if (diskInfo != null) {
state = diskInfo;
}
}
}
@@ -1,300 +0,0 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/utils/upload_speed_calculator.dart';
import 'package:logging/logging.dart';
part 'drift_backup.provider.freezed.dart';
@freezed
abstract class EnqueueStatus with _$EnqueueStatus {
const factory EnqueueStatus({required int enqueueCount, required int totalCount}) = _EnqueueStatus;
}
@freezed
abstract class DriftUploadStatus with _$DriftUploadStatus {
const factory DriftUploadStatus({
required String taskId,
required String filename,
required double progress,
required int fileSize,
required String networkSpeedAsString,
bool? isFailed,
String? error,
}) = _DriftUploadStatus;
}
enum BackupError { none, syncFailed }
@freezed
abstract class DriftBackupState with _$DriftBackupState {
const DriftBackupState._();
const factory DriftBackupState({
required int totalCount,
required int backupCount,
required int remainderCount,
required int processingCount,
required bool isSyncing,
@Default(BackupError.none) BackupError error,
required Map<String, DriftUploadStatus> uploadItems,
@Default({}) Map<String, double> iCloudDownloadProgress,
}) = _DriftBackupState;
int get errorCount => uploadItems.values.where((item) => item.isFailed == true).length;
}
final driftBackupProvider = StateNotifierProvider<DriftBackupNotifier, DriftBackupState>((ref) {
return DriftBackupNotifier(
ref.watch(foregroundUploadServiceProvider),
ref.watch(backgroundUploadServiceProvider),
UploadSpeedManager(),
);
});
class DriftBackupNotifier extends StateNotifier<DriftBackupState> {
DriftBackupNotifier(this._foregroundUploadService, this._backgroundUploadService, this._uploadSpeedManager)
: super(
const DriftBackupState(
totalCount: 0,
backupCount: 0,
remainderCount: 0,
processingCount: 0,
isSyncing: false,
uploadItems: {},
error: BackupError.none,
),
);
final ForegroundUploadService _foregroundUploadService;
final BackgroundUploadService _backgroundUploadService;
final UploadSpeedManager _uploadSpeedManager;
Completer<void>? _cancelToken;
final _logger = Logger("DriftBackupNotifier");
/// Remove upload item from state
void _removeUploadItem(String taskId) {
if (!mounted) {
_logger.warning("Skip _removeUploadItem: notifier disposed");
return;
}
if (state.uploadItems.containsKey(taskId)) {
final updatedItems = Map<String, DriftUploadStatus>.from(state.uploadItems);
updatedItems.remove(taskId);
state = state.copyWith(uploadItems: updatedItems);
}
}
Future<void> getBackupStatus(String userId) async {
if (!mounted) {
_logger.warning("Skip getBackupStatus (pre-call): notifier disposed");
return;
}
final counts = await _foregroundUploadService.getBackupCounts(userId);
if (!mounted) {
_logger.warning("Skip getBackupStatus (post-call): notifier disposed");
return;
}
state = state.copyWith(
totalCount: counts.total,
backupCount: counts.total - counts.remainder,
remainderCount: counts.remainder,
processingCount: counts.processing,
);
}
void updateError(BackupError error) {
if (!mounted) {
_logger.warning("Skip updateError: notifier disposed");
return;
}
state = state.copyWith(error: error);
}
void updateSyncing(bool isSyncing) {
state = state.copyWith(isSyncing: isSyncing);
}
Future<void> startForegroundBackup(String userId) async {
// Cancel any existing backup before starting a new one
if (_cancelToken != null) {
stopForegroundBackup(reason: "restarting the backup");
}
state = state.copyWith(error: BackupError.none);
// A pause during the recount below nulls _cancelToken, so the run keeps its own reference.
final cancelToken = Completer<void>();
_cancelToken = cancelToken;
// Re-baseline the counters against the same DB read that feeds this run's candidate list,
// otherwise a resume counts duplicate successes against the old baseline (#26215).
await getBackupStatus(userId);
return _foregroundUploadService.uploadCandidates(
userId,
cancelToken,
callbacks: UploadCallbacks(
onProgress: _handleForegroundBackupProgress,
onSuccess: _handleForegroundBackupSuccess,
onError: _handleForegroundBackupError,
onICloudProgress: _handleICloudProgress,
),
);
}
void stopForegroundBackup({required String reason}) {
if (_cancelToken != null) {
_logger.info("Foreground backup cancelled: $reason");
}
_cancelToken?.complete();
_cancelToken = null;
_uploadSpeedManager.clear();
state = state.copyWith(uploadItems: {}, iCloudDownloadProgress: {});
}
void _handleICloudProgress(String localAssetId, double progress) {
state = state.copyWith(iCloudDownloadProgress: {...state.iCloudDownloadProgress, localAssetId: progress});
if (progress >= 1.0) {
Future.delayed(const Duration(milliseconds: 250), () {
final updatedProgress = Map<String, double>.from(state.iCloudDownloadProgress);
updatedProgress.remove(localAssetId);
state = state.copyWith(iCloudDownloadProgress: updatedProgress);
});
}
}
void _handleForegroundBackupProgress(String localAssetId, String filename, int bytes, int totalBytes) {
if (_cancelToken == null) {
return;
}
final progress = totalBytes > 0 ? bytes / totalBytes : 0.0;
final networkSpeedAsString = _uploadSpeedManager.updateProgress(localAssetId, bytes, totalBytes);
final currentItem = state.uploadItems[localAssetId];
if (currentItem != null) {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: currentItem.copyWith(
filename: filename,
progress: progress,
fileSize: totalBytes,
networkSpeedAsString: networkSpeedAsString,
),
},
);
} else {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: DriftUploadStatus(
taskId: localAssetId,
filename: filename,
progress: progress,
fileSize: totalBytes,
networkSpeedAsString: networkSpeedAsString,
),
},
);
}
}
void _handleForegroundBackupSuccess(String localAssetId, String remoteAssetId) {
if (!mounted) {
_logger.warning("Skip _handleForegroundBackupSuccess: notifier disposed");
return;
}
state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1);
_uploadSpeedManager.removeTask(localAssetId);
Future.delayed(const Duration(milliseconds: 1000), () {
_removeUploadItem(localAssetId);
});
}
void _handleForegroundBackupError(String localAssetId, String errorMessage) {
_logger.severe("Upload failed for $localAssetId: $errorMessage");
final currentItem = state.uploadItems[localAssetId];
if (currentItem != null) {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: currentItem.copyWith(isFailed: true, error: errorMessage),
},
);
} else {
state = state.copyWith(
uploadItems: {
...state.uploadItems,
localAssetId: DriftUploadStatus(
taskId: localAssetId,
filename: 'Unknown',
progress: 0,
fileSize: 0,
networkSpeedAsString: '',
isFailed: true,
error: errorMessage,
),
},
);
}
_uploadSpeedManager.removeTask(localAssetId);
}
Future<void> startBackupWithURLSession(String userId) async {
if (!mounted) {
_logger.warning("Skip handleBackupResume (pre-call): notifier disposed");
return;
}
_logger.info("Start background backup sequence");
state = state.copyWith(error: BackupError.none);
final tasks = await _backgroundUploadService.getActiveTasks(kBackupGroup);
if (!mounted) {
_logger.warning("Skip handleBackupResume (post-call): notifier disposed");
return;
}
_logger.info("Found ${tasks.length} pending tasks");
if (tasks.isEmpty) {
_logger.info("No pending tasks, starting new upload");
return _backgroundUploadService.uploadBackupCandidates(userId);
}
_logger.info("Resuming upload ${tasks.length} assets");
return _backgroundUploadService.resume();
}
}
final driftBackupCandidateProvider = FutureProvider.autoDispose<List<LocalAsset>>((ref) {
final user = ref.watch(currentUserProvider);
if (user == null) {
return [];
}
return ref.read(foregroundUploadServiceProvider).getBackupCandidates(user.id, onlyHashed: false);
});
final driftCandidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family<List<LocalAlbum>, String>((
ref,
assetId,
) {
return ref
.read(driftProvider)
.localAssetRepository
.getSourceAlbums(assetId, backupSelection: BackupSelection.selected);
});
@@ -7,7 +7,7 @@ import 'package:immich_mobile/domain/services/remote_album.service.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
final localAlbumProvider = FutureProvider<List<LocalAlbum>>(
@@ -23,7 +23,7 @@ final localAlbumThumbnailProvider = FutureProvider.family<LocalAsset?, String>(
final remoteAlbumServiceProvider = Provider<RemoteAlbumService>(
(ref) => RemoteAlbumService(
ref.watch(driftProvider).remoteAlbumRepository,
ref.watch(driftAlbumApiRepositoryProvider),
ref.watch(albumApiRepositoryProvider),
ref.watch(foregroundUploadServiceProvider),
),
);
@@ -6,7 +6,7 @@ import 'package:immich_mobile/domain/services/memory.service.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
final driftMemoryLaneProvider = FutureProvider.autoDispose<List<DriftMemory>>((ref) {
final memoryLaneProvider = FutureProvider.autoDispose<List<Memory>>((ref) {
final (userId, enabled) = ref.watch(currentUserProvider.select((user) => (user?.id, user?.memoryEnabled ?? true)));
if (userId == null || !enabled) {
return const [];
@@ -17,16 +17,16 @@ final driftMemoryLaneProvider = FutureProvider.autoDispose<List<DriftMemory>>((r
final timer = Timer(nextMidnight.difference(now) + const Duration(seconds: 5), ref.invalidateSelf);
ref.onDispose(timer.cancel);
final service = DriftMemoryService(ref.watch(driftProvider).memoryRepository);
final service = MemoryService(ref.watch(driftProvider).memoryRepository);
return service.getMemoryLane(userId);
});
final driftAllMemoriesProvider = FutureProvider.autoDispose.family<List<DriftMemory>, bool>((ref, onlyFavorites) {
final allMemoriesProvider = FutureProvider.autoDispose.family<List<Memory>, bool>((ref, onlyFavorites) {
final (userId, enabled) = ref.watch(currentUserProvider.select((user) => (user?.id, user?.memoryEnabled ?? true)));
if (userId == null || !enabled) {
return const [];
}
final service = DriftMemoryService(ref.watch(driftProvider).memoryRepository);
final service = MemoryService(ref.watch(driftProvider).memoryRepository);
return service.getAll(userId, onlyFavorites: onlyFavorites);
});
@@ -5,17 +5,17 @@ import 'package:immich_mobile/domain/services/people.service.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user_metadata.provider.dart';
final driftPeopleServiceProvider = Provider<DriftPeopleService>(
(ref) => DriftPeopleService(ref.watch(driftProvider).peopleRepository, ref.watch(personApiRepositoryProvider)),
final peopleServiceProvider = Provider<PeopleService>(
(ref) => PeopleService(ref.watch(driftProvider).peopleRepository, ref.watch(personApiRepositoryProvider)),
);
final driftPeopleAssetProvider = FutureProvider.family<List<Person>, String>((ref, assetId) async {
final service = ref.watch(driftPeopleServiceProvider);
final peopleAssetProvider = FutureProvider.family<List<Person>, String>((ref, assetId) async {
final service = ref.watch(peopleServiceProvider);
return service.getAssetPeople(assetId);
});
final getAllPeopleProvider = StreamProvider<List<Person>>((ref) async* {
final service = ref.watch(driftPeopleServiceProvider);
final service = ref.watch(peopleServiceProvider);
final prefs = await ref.watch(userMetadataPreferencesProvider.future);
yield* service.watch(minFaces: prefs?.minimumFaces ?? 3);
});
+1 -1
View File
@@ -8,7 +8,7 @@ bool isRouteInStack(Ref ref, String routeName) {
return router.stackData.any((route) => route.name == routeName);
}
final inLockedViewProvider = Provider<bool>((ref) => isRouteInStack(ref, DriftLockedFolderRoute.name));
final inLockedViewProvider = Provider<bool>((ref) => isRouteInStack(ref, LockedFolderRoute.name));
final isAssetViewerOpenProvider = Provider<bool>((ref) => isRouteInStack(ref, AssetViewerRoute.name));
final currentRouteNameProvider = StateProvider<String?>((ref) => null);
@@ -6,14 +6,12 @@ import 'package:immich_mobile/repositories/api.repository.dart';
// ignore: import_rule_openapi
import 'package:openapi/api.dart' hide AlbumUserRole;
final driftAlbumApiRepositoryProvider = Provider(
(ref) => DriftAlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
);
final albumApiRepositoryProvider = Provider((ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi));
class DriftAlbumApiRepository extends ApiRepository {
class AlbumApiRepository extends ApiRepository {
final AlbumsApi _api;
DriftAlbumApiRepository(this._api);
AlbumApiRepository(this._api);
Future<RemoteAlbum> createDriftAlbum(
String name,
+65 -65
View File
@@ -14,11 +14,11 @@ import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
import 'package:immich_mobile/pages/backup/drift_backup.page.dart';
import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart';
import 'package:immich_mobile/pages/backup/drift_backup_asset_detail.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/backup.page.dart';
import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart';
import 'package:immich_mobile/pages/backup/backup_asset_detail.page.dart';
import 'package:immich_mobile/pages/backup/backup_options.page.dart';
import 'package:immich_mobile/pages/backup/upload_detail.page.dart';
import 'package:immich_mobile/pages/common/app_log.page.dart';
import 'package:immich_mobile/pages/common/app_log_detail.page.dart';
import 'package:immich_mobile/pages/common/headers_settings.page.dart';
@@ -35,41 +35,41 @@ import 'package:immich_mobile/pages/login/login.page.dart';
import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart';
import 'package:immich_mobile/pages/settings/sync_status.page.dart';
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
import 'package:immich_mobile/presentation/pages/activities.page.dart';
import 'package:immich_mobile/presentation/pages/album.page.dart';
import 'package:immich_mobile/presentation/pages/album_options.page.dart';
import 'package:immich_mobile/presentation/pages/archive.page.dart';
import 'package:immich_mobile/presentation/pages/asset_selection_timeline.page.dart';
import 'package:immich_mobile/presentation/pages/asset_troubleshoot.page.dart';
import 'package:immich_mobile/presentation/pages/cleanup_preview.page.dart';
import 'package:immich_mobile/presentation/pages/create_album.page.dart';
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
import 'package:immich_mobile/presentation/pages/download_info.page.dart';
import 'package:immich_mobile/presentation/pages/drift_activities.page.dart';
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart';
import 'package:immich_mobile/presentation/pages/drift_archive.page.dart';
import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart';
import 'package:immich_mobile/presentation/pages/drift_asset_troubleshoot.page.dart';
import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart';
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
import 'package:immich_mobile/presentation/pages/drift_library.page.dart';
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_map.page.dart';
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
import 'package:immich_mobile/presentation/pages/drift_memory_list.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_added.page.dart';
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
import 'package:immich_mobile/presentation/pages/drift_slideshow.page.dart';
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
import 'package:immich_mobile/presentation/pages/edit/drift_edit.page.dart';
import 'package:immich_mobile/presentation/pages/edit/edit.page.dart';
import 'package:immich_mobile/presentation/pages/favorite.page.dart';
import 'package:immich_mobile/presentation/pages/feature_message/whats_new.page.dart';
import 'package:immich_mobile/presentation/pages/library.page.dart';
import 'package:immich_mobile/presentation/pages/local_album.page.dart';
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
import 'package:immich_mobile/presentation/pages/locked_folder.page.dart';
import 'package:immich_mobile/presentation/pages/map.page.dart';
import 'package:immich_mobile/presentation/pages/memory.page.dart';
import 'package:immich_mobile/presentation/pages/memory_list.page.dart';
import 'package:immich_mobile/presentation/pages/partner_detail.page.dart';
import 'package:immich_mobile/presentation/pages/people_collection.page.dart';
import 'package:immich_mobile/presentation/pages/person.page.dart';
import 'package:immich_mobile/presentation/pages/place.page.dart';
import 'package:immich_mobile/presentation/pages/place_detail.page.dart';
import 'package:immich_mobile/presentation/pages/profile/profile_picture_crop.page.dart';
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
import 'package:immich_mobile/presentation/pages/recently_added.page.dart';
import 'package:immich_mobile/presentation/pages/recently_taken.page.dart';
import 'package:immich_mobile/presentation/pages/remote_album.page.dart';
import 'package:immich_mobile/presentation/pages/search/search.page.dart';
import 'package:immich_mobile/presentation/pages/slideshow.page.dart';
import 'package:immich_mobile/presentation/pages/trash.page.dart';
import 'package:immich_mobile/presentation/pages/user_selection.page.dart';
import 'package:immich_mobile/presentation/pages/video.page.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/routing/auth_guard.dart';
@@ -123,9 +123,9 @@ class AppRouter extends RootStackRouter {
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: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false),
AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: AlbumsRoute.page, guards: [_authGuard, _duplicateGuard]),
],
),
AutoRoute(page: ProfilePictureCropRoute.page),
@@ -143,8 +143,8 @@ class AppRouter extends RootStackRouter {
AutoRoute(page: PinAuthRoute.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: BackupRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: BackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard]),
@@ -161,38 +161,38 @@ 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: DriftRecentlyAddedRoute.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: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: FavoriteRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: TrashRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: ArchiveRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: LockedFolderRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]),
AutoRoute(page: VideoRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: AssetSelectionTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: RecentlyAddedRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: LocalAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: CreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PlaceRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: UserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PartnerRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: UploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: SyncStatusRoute.page, guards: [_duplicateGuard]),
AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]),
AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftAlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftMapRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftEditImageRoute.page),
AutoRoute(page: DriftActivitiesRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: PersonRoute.page, guards: [_authGuard]),
AutoRoute(page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: AlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: EditImageRoute.page),
AutoRoute(page: ActivitiesRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: BackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: CleanupPreviewRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftSlideshowRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: DriftMemoryListRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: SlideshowRoute.page, guards: [_authGuard, _duplicateGuard]),
AutoRoute(page: MemoryListRoute.page, guards: [_authGuard, _duplicateGuard]),
// required to handle all deeplinks in deep_link.service.dart
// auto_route_library#1722
RedirectRoute(path: '*', redirectTo: '/'),
+8 -8
View File
@@ -21,8 +21,8 @@ final deepLinkServiceProvider = Provider(
ref.watch(timelineFactoryProvider),
ref.watch(beta_asset_provider.assetServiceProvider),
ref.watch(remoteAlbumServiceProvider),
DriftMemoryService(ref.watch(driftProvider).memoryRepository),
ref.watch(driftPeopleServiceProvider),
MemoryService(ref.watch(driftProvider).memoryRepository),
ref.watch(peopleServiceProvider),
ref.watch(currentUserProvider),
),
);
@@ -31,8 +31,8 @@ class DeepLinkService {
final TimelineFactory _betaTimelineFactory;
final beta_asset_service.AssetService _betaAssetService;
final RemoteAlbumService _betaRemoteAlbumService;
final DriftMemoryService _betaMemoryService;
final DriftPeopleService _betaPeopleService;
final MemoryService _betaMemoryService;
final PeopleService _betaPeopleService;
final UserDto? _currentUser;
@@ -88,7 +88,7 @@ class DeepLinkService {
}
Future<PageRouteInfo?> _buildMemoryDeepLink(String? memoryId) async {
List<DriftMemory> memories = [];
List<Memory> memories = [];
if (memoryId == null) {
if (_currentUser == null) {
@@ -107,7 +107,7 @@ class DeepLinkService {
return null;
}
return DriftMemoryRoute(memories: memories, memoryIndex: 0);
return MemoryRoute(memories: memories, memoryIndex: 0);
}
Future<PageRouteInfo?> _buildAssetDeepLink(String assetId, WidgetRef ref, {String? albumId}) async {
@@ -143,7 +143,7 @@ class DeepLinkService {
return null;
}
return DriftActivitiesRoute(album: album);
return ActivitiesRoute(album: album);
}
Future<PageRouteInfo?> _buildPeopleDeepLink(String personId) async {
@@ -153,6 +153,6 @@ class DeepLinkService {
return null;
}
return DriftPersonRoute(person: person);
return PersonRoute(person: person);
}
}
+2 -2
View File
@@ -39,7 +39,7 @@ Future<String?> showNameEditModal(BuildContext context, Person person) {
context: context,
useRootNavigator: false,
builder: (BuildContext context) {
return DriftPersonNameEditForm(person: person);
return PersonNameEditForm(person: person);
},
);
}
@@ -49,7 +49,7 @@ Future<DateTime?> showBirthdayEditModal(BuildContext context, Person person) {
context: context,
useRootNavigator: false,
builder: (BuildContext context) {
return DriftPersonBirthdayEditForm(person: person);
return PersonBirthdayEditForm(person: person);
},
);
}
+2 -2
View File
@@ -3,8 +3,8 @@ import 'package:immich_mobile/data/server/person.dart';
import 'package:immich_mobile/providers/infrastructure/search.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/activity_api.repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/partner_api.repository.dart';
void invalidateAllApiRepositoryProviders(WidgetRef ref) {
@@ -16,5 +16,5 @@ void invalidateAllApiRepositoryProviders(WidgetRef ref) {
ref.invalidate(searchApiRepositoryProvider);
// Drift
ref.invalidate(driftAlbumApiRepositoryProvider);
ref.invalidate(albumApiRepositoryProvider);
}
@@ -25,7 +25,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget {
}
return GestureDetector(
onTap: () => context.pushRoute(DriftAlbumOptionsRoute(album: currentAlbum)),
onTap: () => context.pushRoute(AlbumOptionsRoute(album: currentAlbum)),
child: SizedBox(
height: 50,
child: ListView.builder(
@@ -12,10 +12,10 @@ import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class DriftAlbumInfoListTile extends HookConsumerWidget {
class AlbumInfoListTile extends HookConsumerWidget {
final LocalAlbum album;
const DriftAlbumInfoListTile({super.key, required this.album});
const AlbumInfoListTile({super.key, required this.album});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -9,7 +9,7 @@ import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/pages/common/settings.page.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup_server.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/locale_provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@@ -29,7 +29,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(localeProvider);
final ServerDiskInfo backupState = ref.watch(backupProvider);
final ServerDiskInfo backupState = ref.watch(backupServerProvider);
final theme = context.themeData;
final bool isHorizontal = !context.isMobile;
final horizontalPadding = isHorizontal ? 100.0 : 20.0;
@@ -38,7 +38,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
useEffect(() {
unawaited(ref.read(backupProvider.notifier).updateDiskInfo());
unawaited(ref.read(backupServerProvider.notifier).updateDiskInfo());
unawaited(ref.read(currentUserProvider.notifier).refresh());
return null;
}, []);
@@ -7,7 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup_server.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/upload_profile_image.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@@ -55,7 +55,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
unawaited(ref.read(currentUserProvider.notifier).refresh());
}
unawaited(ref.read(backupProvider.notifier).updateDiskInfo());
unawaited(ref.read(backupServerProvider.notifier).updateDiskInfo());
}
}
}
@@ -9,7 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
@@ -178,8 +178,8 @@ class _BackupIndicator extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final backupEnabled = ref.watch(appConfigProvider.select((c) => c.backup.enabled));
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
final hasError = ref.watch(backupProvider.select((state) => state.error != BackupError.none));
final isUploading = ref.watch(backupProvider.select((state) => state.uploadItems.isNotEmpty));
final indicatorIcon = _getBackupBadgeIcon(
context,
backupEnabled: backupEnabled,
@@ -188,7 +188,7 @@ class _BackupIndicator extends ConsumerWidget {
);
return IconButton(
onPressed: () => context.pushRoute(const DriftBackupRoute()),
onPressed: () => context.pushRoute(const BackupRoute()),
icon: Badge(
label: indicatorIcon,
backgroundColor: Colors.transparent,
@@ -90,7 +90,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
),
actions: [
IconButton(
onPressed: () => context.pushRoute(DriftSlideshowRoute(timeline: ref.read(timelineServiceProvider))),
onPressed: () => context.pushRoute(SlideshowRoute(timeline: ref.read(timelineServiceProvider))),
icon: Icon(Icons.slideshow_outlined, color: actionIconColor, shadows: actionIconShadows),
),
if (currentAlbum.isActivityEnabled && currentAlbum.isShared)
@@ -18,8 +18,8 @@ import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
class DriftBackupSettings extends StatelessWidget {
const DriftBackupSettings({super.key});
class BackupSettings extends StatelessWidget {
const BackupSettings({super.key});
@override
Widget build(BuildContext context) {
@@ -223,7 +223,7 @@ class _SyncStatsCounts extends ConsumerWidget {
final assetService = ref.watch(assetServiceProvider);
final localAlbumService = LocalAlbumService(db.localAlbumRepository);
final remoteAlbumService = ref.watch(remoteAlbumServiceProvider);
final memoryService = DriftMemoryService(db.memoryRepository);
final memoryService = MemoryService(db.memoryRepository);
final appSettingsService = ref.watch(appSettingsServiceProvider);
Future<List<dynamic>> loadCounts() async {
@@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:mocktail/mocktail.dart';
import '../../infrastructure/repository.mock.dart';
@@ -20,7 +20,7 @@ void main() {
final container = ProviderContainer(
overrides: [
driftProvider.overrideWithValue(drift),
driftAlbumApiRepositoryProvider.overrideWithValue(MockDriftAlbumApiRepository()),
albumApiRepositoryProvider.overrideWithValue(MockAlbumApiRepository()),
storeServiceProvider.overrideWithValue(MockStoreService()),
],
);
@@ -16,7 +16,7 @@ import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.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/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:immich_mobile/repositories/upload.repository.dart';
import 'package:mocktail/mocktail.dart';
@@ -59,4 +59,4 @@ class MockUserApiRepository extends Mock implements UserApiRepository {}
class MockSyncApiRepository extends Mock implements SyncApiRepository {}
class MockDriftAlbumApiRepository extends Mock implements DriftAlbumApiRepository {}
class MockAlbumApiRepository extends Mock implements AlbumApiRepository {}
@@ -1,25 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/presentation/widgets/remote_album/drift_album_option.widget.dart';
import 'package:immich_mobile/presentation/widgets/remote_album/album_option.widget.dart';
import '../../../widget_tester_extensions.dart';
void main() {
group('DriftRemoteAlbumOption', () {
testWidgets('shows kebab menu icon button', (tester) async {
await tester.pumpConsumerWidget(
const DriftRemoteAlbumOption(),
);
await tester.pumpConsumerWidget(const RemoteAlbumOption());
expect(find.byIcon(Icons.more_vert_rounded), findsOneWidget);
});
testWidgets('opens menu when icon button is tapped', (tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -27,15 +21,10 @@ void main() {
expect(find.byIcon(Icons.edit), findsOneWidget);
});
testWidgets('shows edit album option when onEditAlbum is provided',
(tester) async {
testWidgets('shows edit album option when onEditAlbum is provided', (tester) async {
bool editCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () => editCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () => editCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -48,13 +37,8 @@ void main() {
expect(editCalled, isTrue);
});
testWidgets('hides edit album option when onEditAlbum is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onAddPhotos: () {},
),
);
testWidgets('hides edit album option when onEditAlbum is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onAddPhotos: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -62,15 +46,10 @@ void main() {
expect(find.byIcon(Icons.edit), findsNothing);
});
testWidgets('shows add photos option when onAddPhotos is provided',
(tester) async {
testWidgets('shows add photos option when onAddPhotos is provided', (tester) async {
bool addPhotosCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onAddPhotos: () => addPhotosCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onAddPhotos: () => addPhotosCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -83,13 +62,8 @@ void main() {
expect(addPhotosCalled, isTrue);
});
testWidgets('hides add photos option when onAddPhotos is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides add photos option when onAddPhotos is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -97,15 +71,10 @@ void main() {
expect(find.byIcon(Icons.add_a_photo), findsNothing);
});
testWidgets('shows add users option when onAddUsers is provided',
(tester) async {
testWidgets('shows add users option when onAddUsers is provided', (tester) async {
bool addUsersCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onAddUsers: () => addUsersCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onAddUsers: () => addUsersCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -118,13 +87,8 @@ void main() {
expect(addUsersCalled, isTrue);
});
testWidgets('hides add users option when onAddUsers is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides add users option when onAddUsers is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -132,15 +96,10 @@ void main() {
expect(find.byIcon(Icons.group_add), findsNothing);
});
testWidgets('shows leave album option when onLeaveAlbum is provided',
(tester) async {
testWidgets('shows leave album option when onLeaveAlbum is provided', (tester) async {
bool leaveAlbumCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onLeaveAlbum: () => leaveAlbumCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onLeaveAlbum: () => leaveAlbumCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -153,13 +112,8 @@ void main() {
expect(leaveAlbumCalled, isTrue);
});
testWidgets('hides leave album option when onLeaveAlbum is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides leave album option when onLeaveAlbum is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -167,16 +121,10 @@ void main() {
expect(find.byIcon(Icons.person_remove_rounded), findsNothing);
});
testWidgets(
'shows toggle album order option when onToggleAlbumOrder is provided',
(tester) async {
testWidgets('shows toggle album order option when onToggleAlbumOrder is provided', (tester) async {
bool toggleOrderCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onToggleAlbumOrder: () => toggleOrderCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onToggleAlbumOrder: () => toggleOrderCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -189,13 +137,8 @@ void main() {
expect(toggleOrderCalled, isTrue);
});
testWidgets('hides toggle album order option when onToggleAlbumOrder is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides toggle album order option when onToggleAlbumOrder is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -203,16 +146,10 @@ void main() {
expect(find.byIcon(Icons.swap_vert_rounded), findsNothing);
});
testWidgets(
'shows create shared link option when onCreateSharedLink is provided',
(tester) async {
testWidgets('shows create shared link option when onCreateSharedLink is provided', (tester) async {
bool createSharedLinkCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onCreateSharedLink: () => createSharedLinkCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onCreateSharedLink: () => createSharedLinkCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -225,13 +162,8 @@ void main() {
expect(createSharedLinkCalled, isTrue);
});
testWidgets('hides create shared link option when onCreateSharedLink is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides create shared link option when onCreateSharedLink is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -239,15 +171,10 @@ void main() {
expect(find.byIcon(Icons.link), findsNothing);
});
testWidgets('shows options option when onShowOptions is provided',
(tester) async {
testWidgets('shows options option when onShowOptions is provided', (tester) async {
bool showOptionsCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onShowOptions: () => showOptionsCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onShowOptions: () => showOptionsCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -260,13 +187,8 @@ void main() {
expect(showOptionsCalled, isTrue);
});
testWidgets('hides options option when onShowOptions is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides options option when onShowOptions is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -274,15 +196,10 @@ void main() {
expect(find.byIcon(Icons.settings), findsNothing);
});
testWidgets('shows delete album option when onDeleteAlbum is provided',
(tester) async {
testWidgets('shows delete album option when onDeleteAlbum is provided', (tester) async {
bool deleteAlbumCalled = false;
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onDeleteAlbum: () => deleteAlbumCalled = true,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onDeleteAlbum: () => deleteAlbumCalled = true));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -295,13 +212,8 @@ void main() {
expect(deleteAlbumCalled, isTrue);
});
testWidgets('hides delete album option when onDeleteAlbum is null',
(tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
),
);
testWidgets('hides delete album option when onDeleteAlbum is null', (tester) async {
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -310,12 +222,7 @@ void main() {
});
testWidgets('shows divider before delete album option', (tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onEditAlbum: () {},
onDeleteAlbum: () {},
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onEditAlbum: () {}, onDeleteAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -323,10 +230,9 @@ void main() {
expect(find.byType(Divider), findsOneWidget);
});
testWidgets('shows all options when all callbacks are provided',
(tester) async {
testWidgets('shows all options when all callbacks are provided', (tester) async {
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
RemoteAlbumOption(
onEditAlbum: () {},
onAddPhotos: () {},
onAddUsers: () {},
@@ -353,9 +259,7 @@ void main() {
});
testWidgets('shows no options when all callbacks are null', (tester) async {
await tester.pumpConsumerWidget(
const DriftRemoteAlbumOption(),
);
await tester.pumpConsumerWidget(const RemoteAlbumOption());
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -373,11 +277,7 @@ void main() {
testWidgets('uses custom icon color when provided', (tester) async {
const customColor = Colors.red;
await tester.pumpConsumerWidget(
const DriftRemoteAlbumOption(
iconColor: customColor,
),
);
await tester.pumpConsumerWidget(const RemoteAlbumOption(iconColor: customColor));
final iconButton = tester.widget<IconButton>(find.byType(IconButton));
final icon = iconButton.icon as Icon;
@@ -385,11 +285,8 @@ void main() {
expect(icon.color, equals(customColor));
});
testWidgets('uses default white color when iconColor is null',
(tester) async {
await tester.pumpConsumerWidget(
const DriftRemoteAlbumOption(),
);
testWidgets('uses default white color when iconColor is null', (tester) async {
await tester.pumpConsumerWidget(const RemoteAlbumOption());
final iconButton = tester.widget<IconButton>(find.byType(IconButton));
final icon = iconButton.icon as Icon;
@@ -398,15 +295,9 @@ void main() {
});
testWidgets('applies icon shadows when provided', (tester) async {
final shadows = [
const Shadow(offset: Offset(0, 2), blurRadius: 5, color: Colors.black),
];
final shadows = [const Shadow(offset: Offset(0, 2), blurRadius: 5, color: Colors.black)];
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
iconShadows: shadows,
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(iconShadows: shadows));
final iconButton = tester.widget<IconButton>(find.byType(IconButton));
final icon = iconButton.icon as Icon;
@@ -418,7 +309,7 @@ void main() {
testWidgets('owner sees all management options', (tester) async {
// Simulating owner scenario - all callbacks provided
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
RemoteAlbumOption(
onEditAlbum: () {},
onAddPhotos: () {},
onAddUsers: () {},
@@ -443,15 +334,10 @@ void main() {
expect(find.byIcon(Icons.person_remove_rounded), findsNothing);
});
testWidgets('non-owner with editor role sees limited options',
(tester) async {
testWidgets('non-owner with editor role sees limited options', (tester) async {
// Simulating non-owner with editor role - can add photos, show options, leave
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onAddPhotos: () {},
onShowOptions: () {},
onLeaveAlbum: () {},
),
RemoteAlbumOption(onAddPhotos: () {}, onShowOptions: () {}, onLeaveAlbum: () {}),
);
await tester.tap(find.byIcon(Icons.more_vert_rounded));
@@ -473,12 +359,7 @@ void main() {
testWidgets('non-owner viewer sees minimal options', (tester) async {
// Simulating viewer - can only show options and leave
await tester.pumpConsumerWidget(
DriftRemoteAlbumOption(
onShowOptions: () {},
onLeaveAlbum: () {},
),
);
await tester.pumpConsumerWidget(RemoteAlbumOption(onShowOptions: () {}, onLeaveAlbum: () {}));
await tester.tap(find.byIcon(Icons.more_vert_rounded));
await tester.pumpAndSettle();
@@ -9,7 +9,7 @@ import 'package:immich_mobile/models/auth/auth_state.model.dart';
import 'package:immich_mobile/models/server_info/server_version.model.dart';
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
@@ -66,7 +66,7 @@ class TestWebsocketNotifier extends WebsocketNotifier {
void disconnect() => disconnectCount++;
}
class TestDriftBackupNotifier extends DriftBackupNotifier {
class TestDriftBackupNotifier extends BackupNotifier {
TestDriftBackupNotifier() : super(MockForegroundUploadService(), MockBackgroundUploadService(), UploadSpeedManager());
}
@@ -116,7 +116,7 @@ void main() {
websocketProvider.overrideWith((ref) {
return websocket = TestWebsocketNotifier(ref);
}),
driftBackupProvider.overrideWith((_) => TestDriftBackupNotifier()),
backupProvider.overrideWith((_) => TestDriftBackupNotifier()),
backgroundWorkerLockServiceProvider.overrideWithValue(lockService),
],
);
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/utils/upload_speed_calculator.dart';
@@ -14,7 +14,7 @@ class MockBackgroundUploadService extends Mock implements BackgroundUploadServic
void main() {
late MockForegroundUploadService foregroundUploadService;
late MockBackgroundUploadService backgroundUploadService;
late DriftBackupNotifier notifier;
late BackupNotifier notifier;
setUpAll(() {
registerFallbackValue(Completer<void>());
@@ -24,7 +24,7 @@ void main() {
setUp(() {
foregroundUploadService = MockForegroundUploadService();
backgroundUploadService = MockBackgroundUploadService();
notifier = DriftBackupNotifier(foregroundUploadService, backgroundUploadService, UploadSpeedManager());
notifier = BackupNotifier(foregroundUploadService, backgroundUploadService, UploadSpeedManager());
addTearDown(() {
if (notifier.mounted) {
notifier.dispose();
@@ -52,11 +52,11 @@ void main() {
when(() => userService.watchMyUser()).thenAnswer((_) => const Stream.empty());
});
group('driftMemoryLaneProvider', () {
group('memoryLaneProvider', () {
test('re-queries after local midnight', () {
fakeAsync((async) {
final container = makeContainer();
container.listen(driftMemoryLaneProvider, (_, _) {});
container.listen(memoryLaneProvider, (_, _) {});
async.flushMicrotasks();
verify(() => memoryRepository.getAll('user-1')).called(1);
@@ -74,7 +74,7 @@ void main() {
test('cancels the midnight timer when disposed', () {
fakeAsync((async) {
final container = makeContainer();
final subscription = container.listen(driftMemoryLaneProvider, (_, _) {});
final subscription = container.listen(memoryLaneProvider, (_, _) {});
async.flushMicrotasks();
verify(() => memoryRepository.getAll('user-1')).called(1);
@@ -91,7 +91,7 @@ void main() {
fakeAsync((async) {
final container = makeContainer();
container.listen(driftMemoryLaneProvider, (_, _) {});
container.listen(memoryLaneProvider, (_, _) {});
async.flushMicrotasks();
async.elapse(const Duration(hours: 25));
@@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/repositories/album_api_repository.dart';
import 'package:mocktail/mocktail.dart';
import 'package:openapi/api.dart';
@@ -7,7 +7,7 @@ class _MockAlbumsApi extends Mock implements AlbumsApi {}
void main() {
late _MockAlbumsApi api;
late DriftAlbumApiRepository repo;
late AlbumApiRepository repo;
setUpAll(() {
registerFallbackValue(BulkIdsDto(ids: const []));
@@ -15,7 +15,7 @@ void main() {
setUp(() {
api = _MockAlbumsApi();
repo = DriftAlbumApiRepository(api);
repo = AlbumApiRepository(api);
});
void stubResponse(List<BulkIdResponseDto> response) {
@@ -22,9 +22,9 @@ class MockAssetService extends Mock implements AssetService {}
class MockRemoteAlbumService extends Mock implements RemoteAlbumService {}
class MockDriftMemoryService extends Mock implements DriftMemoryService {}
class MockMemoryService extends Mock implements MemoryService {}
class MockDriftPeopleService extends Mock implements DriftPeopleService {}
class MockPeopleService extends Mock implements PeopleService {}
class MockPlatformDeepLink extends Mock implements PlatformDeepLink {}
@@ -62,7 +62,7 @@ final _album = RemoteAlbum(
ownerName: 'Owner',
);
final _memory = DriftMemory(
final _memory = Memory(
id: _memoryId,
createdAt: DateTime(2026, 6, 12),
updatedAt: DateTime(2026, 6, 12),
@@ -85,7 +85,7 @@ void main() {
late MockTimelineFactory timelineFactory;
late MockAssetService assetService;
late MockRemoteAlbumService remoteAlbumService;
late MockDriftMemoryService memoryService;
late MockMemoryService memoryService;
late MockWidgetRef ref;
late List<TimelineService> createdTimelineServices;
late DeepLinkService sut;
@@ -94,7 +94,7 @@ void main() {
timelineFactory = MockTimelineFactory();
assetService = MockAssetService();
remoteAlbumService = MockRemoteAlbumService();
memoryService = MockDriftMemoryService();
memoryService = MockMemoryService();
ref = MockWidgetRef();
createdTimelineServices = [];
@@ -116,7 +116,7 @@ void main() {
assetService,
remoteAlbumService,
memoryService,
MockDriftPeopleService(),
MockPeopleService(),
_user,
);
@@ -170,8 +170,8 @@ void main() {
final route = await sut.handleScheme(deepLink, ref);
expect(route, isA<DriftMemoryRoute>());
expect((route!.args! as DriftMemoryRouteArgs).memories, [_memory]);
expect(route, isA<MemoryRoute>());
expect((route!.args! as MemoryRouteArgs).memories, [_memory]);
verify(() => memoryService.getMemoryLane(_userId)).called(1);
verifyNever(() => memoryService.get(any()));
});
@@ -183,8 +183,8 @@ void main() {
final route = await sut.handleScheme(deepLink, ref);
expect(route, isA<DriftMemoryRoute>());
expect((route!.args! as DriftMemoryRouteArgs).memories, [_memory]);
expect(route, isA<MemoryRoute>());
expect((route!.args! as MemoryRouteArgs).memories, [_memory]);
verifyNever(() => memoryService.getMemoryLane(any()));
verify(() => memoryService.get(_memoryId)).called(1);
});
+1 -1
View File
@@ -31,7 +31,7 @@ class RepositoryMocks {
final remoteExif = RemoteExifRepositoryStub(MockRemoteExifRepository());
final trashedAsset = MockTrashedLocalAssetRepository();
final remoteAlbum = MockRemoteAlbumRepository();
final albumApi = MockDriftAlbumApiRepository();
final albumApi = MockAlbumApiRepository();
final permission = PermissionRepositoryStub(MockPermissionRepository());
final nativeApi = NativeSyncApiStub(MockNativeSyncApi());