mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
wip
This commit is contained in:
parent
429d339c6d
commit
f87ae08cd1
14
mobile/lib/domain/interfaces/backup.interface.dart
Normal file
14
mobile/lib/domain/interfaces/backup.interface.dart
Normal file
@ -0,0 +1,14 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
|
||||
abstract interface class IBackupRepository implements IDatabaseRepository {
|
||||
Future<List<LocalAsset>> getAssets(String albumId);
|
||||
|
||||
Future<List<String>> getAssetIds(String albumId);
|
||||
|
||||
/// Returns the total number of assets that are selected for backup.
|
||||
Future<int> getTotalCount(BackupSelection selection);
|
||||
|
||||
Future<int> getBackupCount();
|
||||
}
|
114
mobile/lib/infrastructure/repositories/backup.repository.dart
Normal file
114
mobile/lib/infrastructure/repositories/backup.repository.dart
Normal file
@ -0,0 +1,114 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
final backupRepositoryProvider = Provider<IBackupRepository>(
|
||||
(ref) => DriftBackupRepository(ref.watch(driftProvider)),
|
||||
);
|
||||
|
||||
class DriftBackupRepository extends DriftDatabaseRepository
|
||||
implements IBackupRepository {
|
||||
final Drift _db;
|
||||
final Platform _platform;
|
||||
const DriftBackupRepository(this._db, {Platform? platform})
|
||||
: _platform = platform ?? const LocalPlatform(),
|
||||
super(_db);
|
||||
|
||||
@override
|
||||
Future<List<LocalAsset>> getAssets(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||
return query
|
||||
.map((row) => row.readTable(_db.localAssetEntity).toDto())
|
||||
.get();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getAssetIds(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
|
||||
return query
|
||||
.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!)
|
||||
.get();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getTotalCount(BackupSelection selection) {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.localAlbumEntity.backupSelection.equals(selection.index),
|
||||
);
|
||||
|
||||
return query.get().then((rows) => rows.length);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getBackupCount() {
|
||||
final query = _db.localAlbumEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAlbumAssetEntity,
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
],
|
||||
)..where(
|
||||
_db.localAlbumEntity.backupSelection.equals(
|
||||
BackupSelection.selected.index,
|
||||
),
|
||||
);
|
||||
|
||||
return query.get().then((rows) => rows.length);
|
||||
}
|
||||
}
|
||||
|
||||
extension on LocalAlbumEntityData {
|
||||
LocalAlbum toDto({int assetCount = 0}) {
|
||||
return LocalAlbum(
|
||||
id: id,
|
||||
name: name,
|
||||
updatedAt: updatedAt,
|
||||
assetCount: assetCount,
|
||||
backupSelection: backupSelection,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on LocalAssetEntityData {
|
||||
LocalAsset toDto() {
|
||||
return LocalAsset(
|
||||
id: id,
|
||||
name: name,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
isFavorite: isFavorite,
|
||||
);
|
||||
}
|
||||
}
|
@ -10,15 +10,14 @@ import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/exp_backup.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
|
||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/services/upload.service.dart';
|
||||
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
|
||||
import 'package:immich_mobile/widgets/backup/current_backup_asset_info_box.dart';
|
||||
import 'package:immich_mobile/widgets/backup/exp_upload_option_toggle.dart';
|
||||
@ -43,6 +42,14 @@ class ExpBackupPage extends HookConsumerWidget {
|
||||
? false
|
||||
: true;
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
ref.read(expBackupProvider.notifier).getBackupStatus();
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
// Update the background settings information just to make sure we
|
||||
@ -88,131 +95,6 @@ class ExpBackupPage extends HookConsumerWidget {
|
||||
[backupState.backupProgress],
|
||||
);
|
||||
|
||||
Widget buildSelectedAlbumName() {
|
||||
String text = "backup_controller_page_backup_selected".tr();
|
||||
final albums = ref
|
||||
.watch(backupAlbumProvider)
|
||||
.where(
|
||||
(album) => album.backupSelection == BackupSelection.selected,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (albums.isNotEmpty) {
|
||||
for (var album in albums) {
|
||||
if (album.name == "Recent" || album.name == "Recents") {
|
||||
text += "${album.name} (${'all'.tr()}), ";
|
||||
} else {
|
||||
text += "${album.name}, ";
|
||||
}
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
text.trim().substring(0, text.length - 2),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
"backup_controller_page_none_selected".tr(),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildExcludedAlbumName() {
|
||||
String text = "backup_controller_page_excluded".tr();
|
||||
final albums = ref
|
||||
.watch(backupAlbumProvider)
|
||||
.where(
|
||||
(album) => album.backupSelection == BackupSelection.excluded,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (albums.isNotEmpty) {
|
||||
for (var album in albums) {
|
||||
text += "${album.name}, ";
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
text.trim().substring(0, text.length - 2),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: Colors.red[300],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
buildFolderSelectionTile() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
side: BorderSide(
|
||||
color: context.colorScheme.outlineVariant,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
elevation: 0,
|
||||
borderOnForeground: false,
|
||||
child: ListTile(
|
||||
minVerticalPadding: 18,
|
||||
title: Text(
|
||||
"backup_controller_page_albums",
|
||||
style: context.textTheme.titleMedium,
|
||||
).tr(),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"backup_controller_page_to_backup",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
).tr(),
|
||||
buildSelectedAlbumName(),
|
||||
buildExcludedAlbumName(),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await context.pushRoute(const ExpBackupAlbumSelectionRoute());
|
||||
// waited until returning from selection
|
||||
await ref
|
||||
.read(backupProvider.notifier)
|
||||
.backupAlbumSelectionDone();
|
||||
// waited until backup albums are stored in DB
|
||||
ref.read(albumProvider.notifier).refreshDeviceAlbums();
|
||||
},
|
||||
child: const Text(
|
||||
"select",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void startBackup() {
|
||||
ref.watch(errorBackupListProvider.notifier).empty();
|
||||
if (ref.watch(backupProvider).backupProgress !=
|
||||
@ -329,61 +211,17 @@ class ExpBackupPage extends HookConsumerWidget {
|
||||
onToggle: () =>
|
||||
context.replaceRoute(const BackupControllerRoute()),
|
||||
),
|
||||
buildFolderSelectionTile(),
|
||||
BackupInfoCard(
|
||||
title: "total".tr(),
|
||||
subtitle: "backup_controller_page_total_sub".tr(),
|
||||
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
||||
? "..."
|
||||
: "${backupState.allUniqueAssets.length}",
|
||||
),
|
||||
BackupInfoCard(
|
||||
title: "backup_controller_page_backup".tr(),
|
||||
subtitle: "backup_controller_page_backup_sub".tr(),
|
||||
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
||||
? "..."
|
||||
: "${backupState.selectedAlbumsBackupAssetsIds.length}",
|
||||
),
|
||||
BackupInfoCard(
|
||||
title: "backup_controller_page_remainder".tr(),
|
||||
subtitle: "backup_controller_page_remainder_sub".tr(),
|
||||
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
||||
? "..."
|
||||
: "${max(0, backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length)}",
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const BackupAlbumSelectionCard(),
|
||||
const TotalCard(),
|
||||
const RemainderCard(),
|
||||
const Divider(),
|
||||
const CurrentUploadingAssetInfoBox(),
|
||||
if (!hasExclusiveAccess) buildBackgroundBackupInfo(),
|
||||
buildBackupButton(),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref.watch(uploadServiceProvider).getRecords();
|
||||
},
|
||||
child: const Text(
|
||||
"get record",
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.watch(uploadServiceProvider)
|
||||
.deleteAllUploadTasks();
|
||||
},
|
||||
child: const Text(
|
||||
"clear records",
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref.watch(uploadServiceProvider).cancelAllUpload();
|
||||
},
|
||||
child: const Text(
|
||||
"cancel all uploads",
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
buildFolderSelectionTile(),
|
||||
const BackupAlbumSelectionCard(),
|
||||
if (!didGetBackupInfo.value) buildLoadingIndicator(),
|
||||
],
|
||||
),
|
||||
@ -393,3 +231,156 @@ class ExpBackupPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BackupAlbumSelectionCard extends ConsumerWidget {
|
||||
const BackupAlbumSelectionCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Widget buildSelectedAlbumName() {
|
||||
String text = "backup_controller_page_backup_selected".tr();
|
||||
final albums = ref
|
||||
.watch(backupAlbumProvider)
|
||||
.where(
|
||||
(album) => album.backupSelection == BackupSelection.selected,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (albums.isNotEmpty) {
|
||||
for (var album in albums) {
|
||||
if (album.name == "Recent" || album.name == "Recents") {
|
||||
text += "${album.name} (${'all'.tr()}), ";
|
||||
} else {
|
||||
text += "${album.name}, ";
|
||||
}
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
text.trim().substring(0, text.length - 2),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
"backup_controller_page_none_selected".tr(),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildExcludedAlbumName() {
|
||||
String text = "backup_controller_page_excluded".tr();
|
||||
final albums = ref
|
||||
.watch(backupAlbumProvider)
|
||||
.where(
|
||||
(album) => album.backupSelection == BackupSelection.excluded,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (albums.isNotEmpty) {
|
||||
for (var album in albums) {
|
||||
text += "${album.name}, ";
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
text.trim().substring(0, text.length - 2),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: Colors.red[300],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
side: BorderSide(
|
||||
color: context.colorScheme.outlineVariant,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
elevation: 0,
|
||||
borderOnForeground: false,
|
||||
child: ListTile(
|
||||
minVerticalPadding: 18,
|
||||
title: Text(
|
||||
"backup_controller_page_albums",
|
||||
style: context.textTheme.titleMedium,
|
||||
).tr(),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"backup_controller_page_to_backup",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
).tr(),
|
||||
buildSelectedAlbumName(),
|
||||
buildExcludedAlbumName(),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await context.pushRoute(const ExpBackupAlbumSelectionRoute());
|
||||
ref.read(expBackupProvider.notifier).getBackupStatus();
|
||||
},
|
||||
child: const Text(
|
||||
"select",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TotalCard extends ConsumerWidget {
|
||||
const TotalCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final totalCount = ref.watch(expBackupProvider.select((p) => p.totalCount));
|
||||
|
||||
return BackupInfoCard(
|
||||
title: "total".tr(),
|
||||
subtitle: "backup_controller_page_total_sub".tr(),
|
||||
info: totalCount.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RemainderCard extends ConsumerWidget {
|
||||
const RemainderCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final backupState = ref.watch(backupProvider);
|
||||
return BackupInfoCard(
|
||||
title: "backup_controller_page_remainder".tr(),
|
||||
subtitle: "backup_controller_page_remainder_sub".tr(),
|
||||
info: backupState.availableAlbums.isEmpty
|
||||
? "..."
|
||||
: "${max(0, backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length)}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ class BackupAlbumNotifier extends StateNotifier<List<LocalAlbum>> {
|
||||
|
||||
Future<void> getAll() async {
|
||||
state = await _localAlbumService.getAll();
|
||||
print("Backup albums loaded: ${state.length}");
|
||||
}
|
||||
|
||||
Future<void> selectAlbum(LocalAlbum album) async {
|
||||
|
73
mobile/lib/providers/backup/exp_backup.provider.dart
Normal file
73
mobile/lib/providers/backup/exp_backup.provider.dart
Normal file
@ -0,0 +1,73 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import 'package:immich_mobile/services/exp_backup.service.dart';
|
||||
|
||||
class ExpBackupState {
|
||||
final int totalCount;
|
||||
ExpBackupState({
|
||||
required this.totalCount,
|
||||
});
|
||||
|
||||
ExpBackupState copyWith({
|
||||
int? totalCount,
|
||||
}) {
|
||||
return ExpBackupState(
|
||||
totalCount: totalCount ?? this.totalCount,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'totalCount': totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
factory ExpBackupState.fromMap(Map<String, dynamic> map) {
|
||||
return ExpBackupState(
|
||||
totalCount: map['totalCount'] as int,
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ExpBackupState.fromJson(String source) =>
|
||||
ExpBackupState.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
@override
|
||||
String toString() => 'ExpBackupState(totalCount: $totalCount)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ExpBackupState other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.totalCount == totalCount;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => totalCount.hashCode;
|
||||
}
|
||||
|
||||
final expBackupProvider =
|
||||
StateNotifierProvider<ExpBackupNotifier, ExpBackupState>((ref) {
|
||||
return ExpBackupNotifier(ref.watch(expBackupServiceProvider));
|
||||
});
|
||||
|
||||
class ExpBackupNotifier extends StateNotifier<ExpBackupState> {
|
||||
ExpBackupNotifier(this._backupService)
|
||||
: super(
|
||||
ExpBackupState(
|
||||
totalCount: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final ExpBackupService _backupService;
|
||||
|
||||
Future<void> getBackupStatus() async {
|
||||
final totalCount = await _backupService.getTotalCount();
|
||||
|
||||
state = state.copyWith(totalCount: totalCount);
|
||||
}
|
||||
}
|
@ -336,5 +336,9 @@ class AppRouter extends RootStackRouter {
|
||||
page: ExpBackupRoute.page,
|
||||
guards: [_authGuard, _duplicateGuard],
|
||||
),
|
||||
AutoRoute(
|
||||
page: ExpBackupAlbumSelectionRoute.page,
|
||||
guards: [_authGuard, _duplicateGuard],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
31
mobile/lib/services/exp_backup.service.dart
Normal file
31
mobile/lib/services/exp_backup.service.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'package:immich_mobile/domain/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
final expBackupServiceProvider = Provider<ExpBackupService>(
|
||||
(ref) => ExpBackupService(
|
||||
ref.watch(backupRepositoryProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class ExpBackupService {
|
||||
ExpBackupService(this._backupRepository);
|
||||
|
||||
final IBackupRepository _backupRepository;
|
||||
|
||||
Future<int> getTotalCount() async {
|
||||
final [selectedCount, excludedCount] = await Future.wait([
|
||||
_backupRepository.getTotalCount(BackupSelection.selected),
|
||||
_backupRepository.getTotalCount(BackupSelection.excluded),
|
||||
]);
|
||||
|
||||
return selectedCount - excludedCount;
|
||||
}
|
||||
|
||||
Future<int> getBackupCount() {
|
||||
return _backupRepository.getBackupCount();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user