Skip to content

Commit ab45c32

Browse files
committed
wip
1 parent f87ae08 commit ab45c32

File tree

7 files changed

+345
-78
lines changed

7 files changed

+345
-78
lines changed

mobile/lib/domain/interfaces/backup.interface.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ abstract interface class IBackupRepository implements IDatabaseRepository {
77

88
Future<List<String>> getAssetIds(String albumId);
99

10-
/// Returns the total number of assets that are selected for backup.
11-
Future<int> getTotalCount(BackupSelection selection);
12-
10+
Future<int> getTotalCount();
11+
Future<int> getRemainderCount();
1312
Future<int> getBackupCount();
13+
14+
Future<List<LocalAlbum>> getBackupAlbums(BackupSelection selectionType);
15+
Future<List<LocalAsset>> getCandidates();
1416
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import 'dart:io';
22

33
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
4+
// ignore: import_rule_photo_manager
5+
import 'package:photo_manager/photo_manager.dart';
46

57
abstract interface class IStorageRepository {
68
Future<File?> getFileForAsset(LocalAsset asset);
9+
Future<AssetEntity?> getAssetEntityForAsset(LocalAsset asset);
710
}

mobile/lib/infrastructure/repositories/backup.repository.dart

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ class DriftBackupRepository extends DriftDatabaseRepository
5151
}
5252

5353
@override
54-
Future<int> getTotalCount(BackupSelection selection) {
54+
Future<int> getTotalCount() async {
55+
final excludedAssetIds = await _getExcludedAssetIds();
56+
5557
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
5658
..addColumns([_db.localAlbumAssetEntity.assetId])
5759
..join([
@@ -61,28 +63,146 @@ class DriftBackupRepository extends DriftDatabaseRepository
6163
),
6264
])
6365
..where(
64-
_db.localAlbumEntity.backupSelection.equals(selection.index),
66+
_db.localAlbumEntity.backupSelection
67+
.equals(BackupSelection.selected.index) &
68+
(excludedAssetIds.isEmpty
69+
? const Constant(true)
70+
: _db.localAlbumAssetEntity.assetId.isNotIn(excludedAssetIds)),
71+
);
72+
73+
return query.get().then((rows) => rows.length);
74+
}
75+
76+
@override
77+
Future<int> getRemainderCount() async {
78+
final excludedAssetIds = await _getExcludedAssetIds();
79+
80+
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
81+
..addColumns(
82+
[_db.localAlbumAssetEntity.assetId],
83+
)
84+
..join([
85+
innerJoin(
86+
_db.localAlbumEntity,
87+
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
88+
),
89+
innerJoin(
90+
_db.localAssetEntity,
91+
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
92+
),
93+
leftOuterJoin(
94+
_db.remoteAssetEntity,
95+
_db.localAssetEntity.checksum
96+
.equalsExp(_db.remoteAssetEntity.checksum),
97+
),
98+
])
99+
..where(
100+
_db.localAlbumEntity.backupSelection
101+
.equals(BackupSelection.selected.index) &
102+
_db.remoteAssetEntity.checksum.isNull() &
103+
(excludedAssetIds.isEmpty
104+
? const Constant(true)
105+
: _db.localAlbumAssetEntity.assetId.isNotIn(excludedAssetIds)),
65106
);
66107

67108
return query.get().then((rows) => rows.length);
68109
}
69110

70111
@override
71-
Future<int> getBackupCount() {
72-
final query = _db.localAlbumEntity.select().join(
112+
Future<int> getBackupCount() async {
113+
final excludedAssetIds = await _getExcludedAssetIds();
114+
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
115+
..addColumns(
116+
[_db.localAlbumAssetEntity.assetId],
117+
)
118+
..join([
119+
innerJoin(
120+
_db.localAlbumEntity,
121+
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
122+
),
123+
innerJoin(
124+
_db.localAssetEntity,
125+
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
126+
),
127+
innerJoin(
128+
_db.remoteAssetEntity,
129+
_db.localAssetEntity.checksum
130+
.equalsExp(_db.remoteAssetEntity.checksum),
131+
),
132+
])
133+
..where(
134+
_db.localAlbumEntity.backupSelection
135+
.equals(BackupSelection.selected.index) &
136+
_db.remoteAssetEntity.checksum.isNotNull() &
137+
(excludedAssetIds.isEmpty
138+
? const Constant(true)
139+
: _db.localAlbumAssetEntity.assetId.isNotIn(excludedAssetIds)),
140+
);
141+
142+
return query.get().then((rows) => rows.length);
143+
}
144+
145+
Future<List<String>> _getExcludedAssetIds() async {
146+
final query = _db.localAlbumAssetEntity.selectOnly()
147+
..addColumns([_db.localAlbumAssetEntity.assetId])
148+
..join([
149+
innerJoin(
150+
_db.localAlbumEntity,
151+
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
152+
),
153+
])
154+
..where(
155+
_db.localAlbumEntity.backupSelection
156+
.equals(BackupSelection.excluded.index),
157+
);
158+
159+
return query
160+
.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!)
161+
.get();
162+
}
163+
164+
@override
165+
Future<List<LocalAlbum>> getBackupAlbums(BackupSelection selectionType) {
166+
final query = _db.localAlbumEntity.select()
167+
..where(
168+
(tbl) => tbl.backupSelection.equals(selectionType.index),
169+
);
170+
171+
return query.map((localAlbum) => localAlbum.toDto(assetCount: 0)).get();
172+
}
173+
174+
@override
175+
Future<List<LocalAsset>> getCandidates() async {
176+
final excludedAssetIds = await _getExcludedAssetIds();
177+
178+
final query = _db.localAlbumAssetEntity.select(distinct: true).join(
73179
[
74180
innerJoin(
75-
_db.localAlbumAssetEntity,
181+
_db.localAlbumEntity,
76182
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
77183
),
184+
innerJoin(
185+
_db.localAssetEntity,
186+
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
187+
),
188+
leftOuterJoin(
189+
_db.remoteAssetEntity,
190+
_db.localAssetEntity.checksum
191+
.equalsExp(_db.remoteAssetEntity.checksum),
192+
),
78193
],
79194
)..where(
80-
_db.localAlbumEntity.backupSelection.equals(
81-
BackupSelection.selected.index,
82-
),
195+
_db.localAlbumEntity.backupSelection
196+
.equals(BackupSelection.selected.index) &
197+
_db.remoteAssetEntity.checksum.isNull() &
198+
(excludedAssetIds.isEmpty
199+
? const Constant(true)
200+
: _db.localAlbumAssetEntity.assetId.isNotIn(excludedAssetIds)),
83201
);
84202

85-
return query.get().then((rows) => rows.length);
203+
return query
204+
.map((row) => row.readTable(_db.localAssetEntity).toDto())
205+
.get();
86206
}
87207
}
88208

mobile/lib/infrastructure/repositories/storage.repository.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,24 @@ class StorageRepository implements IStorageRepository {
2828
}
2929
return file;
3030
}
31+
32+
@override
33+
Future<AssetEntity?> getAssetEntityForAsset(LocalAsset asset) async {
34+
AssetEntity? entity;
35+
try {
36+
entity = await AssetEntity.fromId(asset.id);
37+
if (entity == null) {
38+
_log.warning(
39+
"Cannot get AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
40+
);
41+
}
42+
} catch (error, stackTrace) {
43+
_log.warning(
44+
"Error getting AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
45+
error,
46+
stackTrace,
47+
);
48+
}
49+
return entity;
50+
}
3151
}

mobile/lib/pages/backup/exp_backup_controller.page.dart

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -95,55 +95,20 @@ class ExpBackupPage extends HookConsumerWidget {
9595
[backupState.backupProgress],
9696
);
9797

98-
void startBackup() {
99-
ref.watch(errorBackupListProvider.notifier).empty();
100-
if (ref.watch(backupProvider).backupProgress !=
101-
BackUpProgressEnum.inBackground) {
102-
ref.watch(backupProvider.notifier).startBackupProcess();
103-
}
104-
}
105-
10698
Widget buildBackupButton() {
10799
return Padding(
108100
padding: const EdgeInsets.only(
109101
top: 24,
110102
),
111-
child: Container(
112-
child: backupState.backupProgress == BackUpProgressEnum.inProgress ||
113-
backupState.backupProgress ==
114-
BackUpProgressEnum.manualInProgress
115-
? ElevatedButton(
116-
style: ElevatedButton.styleFrom(
117-
foregroundColor: Colors.grey[50],
118-
backgroundColor: Colors.red[300],
119-
// padding: const EdgeInsets.all(14),
120-
),
121-
onPressed: () {
122-
if (backupState.backupProgress ==
123-
BackUpProgressEnum.manualInProgress) {
124-
ref.read(manualUploadProvider.notifier).cancelBackup();
125-
} else {
126-
ref.read(backupProvider.notifier).cancelBackup();
127-
}
128-
},
129-
child: const Text(
130-
"cancel",
131-
style: TextStyle(
132-
fontSize: 14,
133-
fontWeight: FontWeight.bold,
134-
),
135-
).tr(),
136-
)
137-
: ElevatedButton(
138-
onPressed: shouldBackup ? startBackup : null,
139-
child: const Text(
140-
"backup_controller_page_start_backup",
141-
style: TextStyle(
142-
fontSize: 16,
143-
fontWeight: FontWeight.bold,
144-
),
145-
).tr(),
146-
),
103+
child: ElevatedButton(
104+
onPressed: () => ref.read(expBackupProvider.notifier).backup(),
105+
child: const Text(
106+
"backup_controller_page_start_backup",
107+
style: TextStyle(
108+
fontSize: 16,
109+
fontWeight: FontWeight.bold,
110+
),
111+
).tr(),
147112
),
148113
);
149114
}
@@ -214,6 +179,7 @@ class ExpBackupPage extends HookConsumerWidget {
214179
const SizedBox(height: 8),
215180
const BackupAlbumSelectionCard(),
216181
const TotalCard(),
182+
const BackupCard(),
217183
const RemainderCard(),
218184
const Divider(),
219185
const CurrentUploadingAssetInfoBox(),
@@ -369,18 +335,33 @@ class TotalCard extends ConsumerWidget {
369335
}
370336
}
371337

338+
class BackupCard extends ConsumerWidget {
339+
const BackupCard({super.key});
340+
341+
@override
342+
Widget build(BuildContext context, WidgetRef ref) {
343+
final backupCount =
344+
ref.watch(expBackupProvider.select((p) => p.backupCount));
345+
346+
return BackupInfoCard(
347+
title: "backup_controller_page_backup".tr(),
348+
subtitle: "backup_controller_page_backup_sub".tr(),
349+
info: backupCount.toString(),
350+
);
351+
}
352+
}
353+
372354
class RemainderCard extends ConsumerWidget {
373355
const RemainderCard({super.key});
374356

375357
@override
376358
Widget build(BuildContext context, WidgetRef ref) {
377-
final backupState = ref.watch(backupProvider);
359+
final remainderCount =
360+
ref.watch(expBackupProvider.select((p) => p.remainderCount));
378361
return BackupInfoCard(
379362
title: "backup_controller_page_remainder".tr(),
380363
subtitle: "backup_controller_page_remainder_sub".tr(),
381-
info: backupState.availableAlbums.isEmpty
382-
? "..."
383-
: "${max(0, backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length)}",
364+
info: remainderCount.toString(),
384365
);
385366
}
386367
}

0 commit comments

Comments
 (0)