mirror of
https://github.com/samsonjs/immich.git
synced 2026-03-31 10:15:54 +00:00
fix: show only local assets from albums selected for backup (#20050)
* show only local assets from albums selected for backup # Conflicts: # mobile/lib/infrastructure/repositories/db.repository.drift.dart * ignore backup selection * fix: backup album ownerId * fix: backup album ownerId * only show local only assets that are selected for backup * add index on visibility and backup selection * fix: video not playing in search view * remove safe area from bottom bar * refactor stack count with a CTE and local asset with a SELECT * fix lint * remove redundant COALESCE * remove stack count from main timeline query --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
92384c28de
commit
08122d6871
19 changed files with 152 additions and 186 deletions
BIN
mobile/drift_schemas/main/drift_schema_v4.json
generated
BIN
mobile/drift_schemas/main/drift_schema_v4.json
generated
Binary file not shown.
|
|
@ -15,7 +15,6 @@ class RemoteAsset extends BaseAsset {
|
|||
final AssetVisibility visibility;
|
||||
final String ownerId;
|
||||
final String? stackId;
|
||||
final int stackCount;
|
||||
|
||||
const RemoteAsset({
|
||||
required this.id,
|
||||
|
|
@ -34,7 +33,6 @@ class RemoteAsset extends BaseAsset {
|
|||
this.visibility = AssetVisibility.timeline,
|
||||
super.livePhotoVideoId,
|
||||
this.stackId,
|
||||
this.stackCount = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -61,7 +59,6 @@ class RemoteAsset extends BaseAsset {
|
|||
thumbHash: ${thumbHash ?? "<NA>"},
|
||||
visibility: $visibility,
|
||||
stackId: ${stackId ?? "<NA>"},
|
||||
stackCount: $stackCount,
|
||||
checksum: $checksum,
|
||||
livePhotoVideoId: ${livePhotoVideoId ?? "<NA>"},
|
||||
}''';
|
||||
|
|
@ -77,8 +74,7 @@ class RemoteAsset extends BaseAsset {
|
|||
ownerId == other.ownerId &&
|
||||
thumbHash == other.thumbHash &&
|
||||
visibility == other.visibility &&
|
||||
stackId == other.stackId &&
|
||||
stackCount == other.stackCount;
|
||||
stackId == other.stackId;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -89,8 +85,7 @@ class RemoteAsset extends BaseAsset {
|
|||
localId.hashCode ^
|
||||
thumbHash.hashCode ^
|
||||
visibility.hashCode ^
|
||||
stackId.hashCode ^
|
||||
stackCount.hashCode;
|
||||
stackId.hashCode;
|
||||
|
||||
RemoteAsset copyWith({
|
||||
String? id,
|
||||
|
|
@ -109,7 +104,6 @@ class RemoteAsset extends BaseAsset {
|
|||
AssetVisibility? visibility,
|
||||
String? livePhotoVideoId,
|
||||
String? stackId,
|
||||
int? stackCount,
|
||||
}) {
|
||||
return RemoteAsset(
|
||||
id: id ?? this.id,
|
||||
|
|
@ -128,7 +122,6 @@ class RemoteAsset extends BaseAsset {
|
|||
visibility: visibility ?? this.visibility,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
stackId: stackId ?? this.stackId,
|
||||
stackCount: stackCount ?? this.stackCount,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +1,68 @@
|
|||
import 'remote_asset.entity.dart';
|
||||
import 'local_asset.entity.dart';
|
||||
import 'stack.entity.dart';
|
||||
import 'local_asset.entity.dart';
|
||||
import 'local_album.entity.dart';
|
||||
import 'local_album_asset.entity.dart';
|
||||
|
||||
mergedAsset: SELECT * FROM
|
||||
(
|
||||
SELECT
|
||||
rae.id as remote_id,
|
||||
lae.id as local_id,
|
||||
rae.name,
|
||||
rae."type",
|
||||
rae.created_at,
|
||||
rae.updated_at,
|
||||
rae.width,
|
||||
rae.height,
|
||||
rae.duration_in_seconds,
|
||||
rae.is_favorite,
|
||||
rae.thumb_hash,
|
||||
rae.checksum,
|
||||
rae.owner_id,
|
||||
rae.live_photo_video_id,
|
||||
0 as orientation,
|
||||
rae.stack_id,
|
||||
COALESCE(stack_count.total_count, 0) AS stack_count
|
||||
FROM
|
||||
remote_asset_entity rae
|
||||
LEFT JOIN
|
||||
local_asset_entity lae ON rae.checksum = lae.checksum
|
||||
LEFT JOIN
|
||||
stack_entity se ON rae.stack_id = se.id
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
stack_id,
|
||||
COUNT(*) AS total_count
|
||||
FROM remote_asset_entity
|
||||
WHERE deleted_at IS NULL
|
||||
AND visibility = 0
|
||||
AND stack_id IS NOT NULL
|
||||
GROUP BY stack_id
|
||||
) AS stack_count ON rae.stack_id = stack_count.stack_id
|
||||
WHERE
|
||||
rae.deleted_at IS NULL
|
||||
AND rae.visibility = 0
|
||||
AND rae.owner_id in ?
|
||||
AND (
|
||||
rae.stack_id IS NULL
|
||||
OR rae.id = se.primary_asset_id
|
||||
)
|
||||
UNION ALL
|
||||
SELECT
|
||||
NULL as remote_id,
|
||||
lae.id as local_id,
|
||||
lae.name,
|
||||
lae."type",
|
||||
lae.created_at,
|
||||
lae.updated_at,
|
||||
lae.width,
|
||||
lae.height,
|
||||
lae.duration_in_seconds,
|
||||
lae.is_favorite,
|
||||
NULL as thumb_hash,
|
||||
lae.checksum,
|
||||
NULL as owner_id,
|
||||
NULL as live_photo_video_id,
|
||||
lae.orientation,
|
||||
NULL as stack_id,
|
||||
0 AS stack_count
|
||||
FROM
|
||||
local_asset_entity lae
|
||||
LEFT JOIN
|
||||
remote_asset_entity rae ON rae.checksum = lae.checksum
|
||||
WHERE
|
||||
rae.id IS NULL
|
||||
mergedAsset:
|
||||
SELECT
|
||||
rae.id as remote_id,
|
||||
(SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id,
|
||||
rae.name,
|
||||
rae."type",
|
||||
rae.created_at as created_at,
|
||||
rae.updated_at,
|
||||
rae.width,
|
||||
rae.height,
|
||||
rae.duration_in_seconds,
|
||||
rae.is_favorite,
|
||||
rae.thumb_hash,
|
||||
rae.checksum,
|
||||
rae.owner_id,
|
||||
rae.live_photo_video_id,
|
||||
0 as orientation,
|
||||
rae.stack_id
|
||||
FROM
|
||||
remote_asset_entity rae
|
||||
LEFT JOIN
|
||||
stack_entity se ON rae.stack_id = se.id
|
||||
WHERE
|
||||
rae.deleted_at IS NULL
|
||||
AND rae.visibility = 0 -- timeline visibility
|
||||
AND rae.owner_id in ?
|
||||
AND (
|
||||
rae.stack_id IS NULL
|
||||
OR rae.id = se.primary_asset_id
|
||||
)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
NULL as remote_id,
|
||||
lae.id as local_id,
|
||||
lae.name,
|
||||
lae."type",
|
||||
lae.created_at as created_at,
|
||||
lae.updated_at,
|
||||
lae.width,
|
||||
lae.height,
|
||||
lae.duration_in_seconds,
|
||||
lae.is_favorite,
|
||||
NULL as thumb_hash,
|
||||
lae.checksum,
|
||||
NULL as owner_id,
|
||||
NULL as live_photo_video_id,
|
||||
lae.orientation,
|
||||
NULL as stack_id
|
||||
FROM
|
||||
local_asset_entity lae
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $limit;
|
||||
|
|
@ -85,17 +77,14 @@ SELECT
|
|||
FROM
|
||||
(
|
||||
SELECT
|
||||
rae.name,
|
||||
rae.created_at
|
||||
FROM
|
||||
remote_asset_entity rae
|
||||
LEFT JOIN
|
||||
local_asset_entity lae ON rae.checksum = lae.checksum
|
||||
LEFT JOIN
|
||||
stack_entity se ON rae.stack_id = se.id
|
||||
WHERE
|
||||
rae.deleted_at IS NULL
|
||||
AND rae.visibility = 0
|
||||
AND rae.visibility = 0 -- timeline visibility
|
||||
AND rae.owner_id in ?
|
||||
AND (
|
||||
rae.stack_id IS NULL
|
||||
|
|
@ -103,14 +92,18 @@ FROM
|
|||
)
|
||||
UNION ALL
|
||||
SELECT
|
||||
lae.name,
|
||||
lae.created_at
|
||||
FROM
|
||||
local_asset_entity lae
|
||||
LEFT JOIN
|
||||
remote_asset_entity rae ON rae.checksum = lae.checksum
|
||||
LEFT JOIN
|
||||
local_album_asset_entity laa ON laa.asset_id = lae.id
|
||||
LEFT JOIN
|
||||
local_album_entity la ON la.id = laa.album_id
|
||||
WHERE
|
||||
rae.id IS NULL
|
||||
AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
GROUP BY bucket_date
|
||||
ORDER BY bucket_date DESC;
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -50,7 +50,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
return query.get().then((rows) => rows.length);
|
||||
}
|
||||
|
||||
Future<int> getRemainderCount() async {
|
||||
Future<int> getRemainderCount(String userId) async {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
|
|
@ -74,7 +74,8 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
..where(
|
||||
_db.localAlbumEntity.backupSelection
|
||||
.equalsValue(BackupSelection.selected) &
|
||||
_db.remoteAssetEntity.id.isNull() &
|
||||
(_db.remoteAssetEntity.id.isNull() |
|
||||
_db.remoteAssetEntity.ownerId.equals(userId).not()) &
|
||||
_db.localAlbumAssetEntity.assetId
|
||||
.isNotInQuery(_getExcludedSubquery()),
|
||||
);
|
||||
|
|
@ -82,7 +83,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
return query.get().then((rows) => rows.length);
|
||||
}
|
||||
|
||||
Future<int> getBackupCount() async {
|
||||
Future<int> getBackupCount(String userId) async {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
|
||||
..addColumns(
|
||||
[_db.localAlbumAssetEntity.assetId],
|
||||
|
|
@ -109,6 +110,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
_db.localAlbumEntity.backupSelection
|
||||
.equalsValue(BackupSelection.selected) &
|
||||
_db.remoteAssetEntity.id.isNotNull() &
|
||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
_db.localAlbumAssetEntity.assetId
|
||||
.isNotInQuery(_getExcludedSubquery()),
|
||||
);
|
||||
|
|
@ -116,7 +118,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
return query.get().then((rows) => rows.length);
|
||||
}
|
||||
|
||||
Future<List<LocalAsset>> getCandidates() async {
|
||||
Future<List<LocalAsset>> getCandidates(String userId) async {
|
||||
final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true)
|
||||
..addColumns([_db.localAlbumEntity.id])
|
||||
..where(
|
||||
|
|
@ -141,6 +143,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
..addColumns([_db.remoteAssetEntity.checksum])
|
||||
..where(
|
||||
_db.remoteAssetEntity.checksum.equalsExp(lae.checksum) &
|
||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
lae.checksum.isNotNull(),
|
||||
),
|
||||
) &
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import 'package:drift/drift.dart';
|
|||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
|
|
@ -97,7 +97,9 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||
await m.alterTable(TableMigration(v3.stackEntity));
|
||||
},
|
||||
from3To4: (m, v4) async {
|
||||
// Thumbnail path column got removed from person_entity
|
||||
await m.alterTable(TableMigration(v4.personEntity));
|
||||
// asset_face_entity is added
|
||||
await m.create(v4.assetFaceEntity);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -32,49 +32,21 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Stream<RemoteAsset?> watchAsset(String id) {
|
||||
final stackCountRef = _db.stackEntity.id.count();
|
||||
|
||||
final query = _db.remoteAssetEntity.select().addColumns([
|
||||
_db.localAssetEntity.id,
|
||||
_db.stackEntity.primaryAssetId,
|
||||
stackCountRef,
|
||||
]).join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.stackEntity,
|
||||
_db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity.createAlias('stacked_assets'),
|
||||
_db.stackEntity.id.equalsExp(
|
||||
_db.remoteAssetEntity.createAlias('stacked_assets').stackId,
|
||||
),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.id.equals(id))
|
||||
..groupBy([
|
||||
_db.remoteAssetEntity.id,
|
||||
_db.localAssetEntity.id,
|
||||
_db.stackEntity.primaryAssetId,
|
||||
])
|
||||
..limit(1);
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.remoteAssetEntity).toDto();
|
||||
final primaryAssetId = row.read(_db.stackEntity.primaryAssetId);
|
||||
final stackCount =
|
||||
primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0;
|
||||
|
||||
return asset.copyWith(
|
||||
localId: row.read(_db.localAssetEntity.id),
|
||||
stackCount: stackCount,
|
||||
);
|
||||
return asset.copyWith(localId: row.read(_db.localAssetEntity.id));
|
||||
}).watchSingleOrNull();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
required int count,
|
||||
}) {
|
||||
return _db.mergedAssetDrift
|
||||
.mergedAsset(userIds, limit: Limit(count, offset))
|
||||
.mergedAsset(userIds, limit: (_) => Limit(count, offset))
|
||||
.map(
|
||||
(row) => row.remoteId != null && row.ownerId != null
|
||||
? RemoteAsset(
|
||||
|
|
@ -90,7 +90,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
durationInSeconds: row.durationInSeconds,
|
||||
livePhotoVideoId: row.livePhotoVideoId,
|
||||
stackId: row.stackId,
|
||||
stackCount: row.stackCount,
|
||||
)
|
||||
: LocalAsset(
|
||||
id: row.localId!,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
|
|||
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart';
|
||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
|
||||
|
||||
|
|
@ -24,12 +25,24 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
ref.read(driftBackupProvider.notifier).getBackupStatus();
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||
}
|
||||
|
||||
Future<void> startBackup() async {
|
||||
await ref.read(driftBackupProvider.notifier).getBackupStatus();
|
||||
await ref.read(driftBackupProvider.notifier).backup();
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
await ref.read(driftBackupProvider.notifier).backup(currentUser.id);
|
||||
}
|
||||
|
||||
Future<void> stopBackup() async {
|
||||
|
|
@ -207,7 +220,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
|
|||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await context.pushRoute(const DriftBackupAlbumSelectionRoute());
|
||||
ref.read(driftBackupProvider.notifier).getBackupStatus();
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
},
|
||||
child: const Text(
|
||||
"select",
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import 'package:auto_route/auto_route.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
|
||||
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
||||
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
||||
|
||||
@RoutePage()
|
||||
class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
|
||||
|
|
@ -92,7 +92,15 @@ class _DriftBackupAlbumSelectionPageState
|
|||
if (didPop && !_hasPopped) {
|
||||
_hasPopped = true;
|
||||
|
||||
await ref.read(driftBackupProvider.notifier).getBackupStatus();
|
||||
super.initState();
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
final currentTotalAssetCount =
|
||||
ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||
|
||||
|
|
@ -107,7 +115,7 @@ class _DriftBackupAlbumSelectionPageState
|
|||
final backupNotifier = ref.read(driftBackupProvider.notifier);
|
||||
|
||||
backupNotifier.cancel().then((_) {
|
||||
backupNotifier.backup();
|
||||
backupNotifier.backup(currentUser.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
|||
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
||||
import 'package:immich_mobile/providers/tab.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
|
|
@ -38,7 +39,14 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
|
|||
|
||||
await runNewSync(ref, full: true).then((_) async {
|
||||
if (isEnableBackup) {
|
||||
await ref.read(driftBackupProvider.notifier).handleBackupResume();
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.handleBackupResume(currentUser.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,11 +6,7 @@ class StackChildrenNotifier
|
|||
extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
|
||||
@override
|
||||
Future<List<RemoteAsset>> build(BaseAsset? asset) async {
|
||||
if (asset == null ||
|
||||
asset is! RemoteAsset ||
|
||||
asset.stackId == null ||
|
||||
// The stackCount check is to ensure we only fetch stacks for timelines that have stacks
|
||||
asset.stackCount == 0) {
|
||||
if (asset == null || asset is! RemoteAsset || asset.stackId == null) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
: const BoxDecoration();
|
||||
|
||||
final hasStack =
|
||||
asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0;
|
||||
asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
|
|
@ -86,9 +86,7 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
right: 10.0,
|
||||
top: asset.isVideo ? 24.0 : 6.0,
|
||||
),
|
||||
child: _StackIndicator(
|
||||
stackCount: (asset as RemoteAsset).stackCount,
|
||||
),
|
||||
child: const _TileOverlayIcon(Icons.burst_mode_rounded),
|
||||
),
|
||||
),
|
||||
if (asset.isVideo)
|
||||
|
|
@ -198,40 +196,6 @@ class _SelectionIndicator extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _StackIndicator extends StatelessWidget {
|
||||
final int stackCount;
|
||||
|
||||
const _StackIndicator({required this.stackCount});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
spacing: 3,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
// CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
stackCount.toString(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
shadows: [
|
||||
Shadow(
|
||||
blurRadius: 5.0,
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const _TileOverlayIcon(Icons.burst_mode_rounded),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _VideoIndicator extends StatelessWidget {
|
||||
final Duration duration;
|
||||
const _VideoIndicator(this.duration);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/memory.provider.dart';
|
|||
import 'package:immich_mobile/providers/notification_permission.provider.dart';
|
||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/providers/tab.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/services/background.service.dart';
|
||||
|
|
@ -110,7 +111,14 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||
.getSetting(AppSettingsEnum.enableBackup);
|
||||
|
||||
if (isEnableBackup) {
|
||||
await _ref.read(driftBackupProvider.notifier).handleBackupResume();
|
||||
final currentUser = _ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await _ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.handleBackupResume(currentUser.id);
|
||||
}
|
||||
});
|
||||
} catch (e, stackTrace) {
|
||||
|
|
|
|||
|
|
@ -329,11 +329,11 @@ class ExpBackupNotifier extends StateNotifier<DriftBackupState> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> getBackupStatus() async {
|
||||
Future<void> getBackupStatus(String userId) async {
|
||||
final [totalCount, backupCount, remainderCount] = await Future.wait([
|
||||
_backupService.getTotalCount(),
|
||||
_backupService.getBackupCount(),
|
||||
_backupService.getRemainderCount(),
|
||||
_backupService.getBackupCount(userId),
|
||||
_backupService.getRemainderCount(userId),
|
||||
]);
|
||||
|
||||
state = state.copyWith(
|
||||
|
|
@ -343,8 +343,8 @@ class ExpBackupNotifier extends StateNotifier<DriftBackupState> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> backup() {
|
||||
return _backupService.backup(_updateEnqueueCount);
|
||||
Future<void> backup(String userId) {
|
||||
return _backupService.backup(userId, _updateEnqueueCount);
|
||||
}
|
||||
|
||||
void _updateEnqueueCount(EnqueueStatus status) {
|
||||
|
|
@ -379,11 +379,11 @@ class ExpBackupNotifier extends StateNotifier<DriftBackupState> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> handleBackupResume() async {
|
||||
Future<void> handleBackupResume(String userId) async {
|
||||
final tasks = await FileDownloader().allTasks(group: kBackupGroup);
|
||||
if (tasks.isEmpty) {
|
||||
// Start a new backup queue
|
||||
await backup();
|
||||
await backup(userId);
|
||||
}
|
||||
|
||||
debugPrint("Tasks to resume: ${tasks.length}");
|
||||
|
|
|
|||
|
|
@ -48,20 +48,21 @@ class DriftBackupService {
|
|||
return _backupRepository.getTotalCount();
|
||||
}
|
||||
|
||||
Future<int> getRemainderCount() {
|
||||
return _backupRepository.getRemainderCount();
|
||||
Future<int> getRemainderCount(String userId) {
|
||||
return _backupRepository.getRemainderCount(userId);
|
||||
}
|
||||
|
||||
Future<int> getBackupCount() {
|
||||
return _backupRepository.getBackupCount();
|
||||
Future<int> getBackupCount(String userId) {
|
||||
return _backupRepository.getBackupCount(userId);
|
||||
}
|
||||
|
||||
Future<void> backup(
|
||||
String userId,
|
||||
void Function(EnqueueStatus status) onEnqueueTasks,
|
||||
) async {
|
||||
shouldCancel = false;
|
||||
|
||||
final candidates = await _backupRepository.getCandidates();
|
||||
final candidates = await _backupRepository.getCandidates(userId);
|
||||
if (candidates.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
BIN
mobile/test/drift/main/generated/schema_v4.dart
generated
BIN
mobile/test/drift/main/generated/schema_v4.dart
generated
Binary file not shown.
Loading…
Reference in a new issue