mirror of
https://github.com/immich-app/immich
synced 2026-08-22 13:13:05 +00:00
fix(mobile): refresh memories on resume and day change (#28983)
* fix(mobile): refresh memories on resume and day change * fix(mobile): invalidate memories once after the resume sync
This commit is contained in:
parent
769c4015d3
commit
793487e52c
3 changed files with 105 additions and 0 deletions
|
|
@ -9,6 +9,7 @@ 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/gallery_permission.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/memory.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/permission.provider.dart';
|
||||
|
|
@ -115,6 +116,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||
_safeRun(backgroundManager.syncLocal(full: CurrentPlatform.isAndroid ? true : false), "syncLocal"),
|
||||
_safeRun(backgroundManager.syncRemote().then((success) => syncSuccess = success), "syncRemote"),
|
||||
]);
|
||||
_ref.invalidate(driftMemoryFutureProvider);
|
||||
if (syncSuccess) {
|
||||
await Future.wait([
|
||||
_safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/memory.model.dart';
|
||||
import 'package:immich_mobile/domain/services/memory.service.dart';
|
||||
|
|
@ -19,6 +21,11 @@ final driftMemoryFutureProvider = FutureProvider.autoDispose<List<DriftMemory>>(
|
|||
return const [];
|
||||
}
|
||||
|
||||
final now = DateTime.now();
|
||||
final nextMidnight = DateTime(now.year, now.month, now.day + 1);
|
||||
final timer = Timer(nextMidnight.difference(now) + const Duration(seconds: 5), ref.invalidateSelf);
|
||||
ref.onDispose(timer.cancel);
|
||||
|
||||
final service = ref.watch(driftMemoryServiceProvider);
|
||||
return service.getMemoryLane(userId);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
import 'package:fake_async/fake_async.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/memory.service.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockDriftMemoryService extends Mock implements DriftMemoryService {}
|
||||
|
||||
class MockUserService extends Mock implements UserService {}
|
||||
|
||||
void main() {
|
||||
late MockDriftMemoryService memoryService;
|
||||
late MockUserService userService;
|
||||
|
||||
UserDto user({bool memoryEnabled = true}) => UserDto(
|
||||
id: 'user-1',
|
||||
email: 'user@test.dev',
|
||||
name: 'user',
|
||||
memoryEnabled: memoryEnabled,
|
||||
profileChangedAt: DateTime(2026),
|
||||
);
|
||||
|
||||
ProviderContainer makeContainer() {
|
||||
final container = ProviderContainer(
|
||||
overrides: [
|
||||
driftMemoryServiceProvider.overrideWithValue(memoryService),
|
||||
currentUserProvider.overrideWith((ref) => CurrentUserProvider(userService)),
|
||||
],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
return container;
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
memoryService = MockDriftMemoryService();
|
||||
userService = MockUserService();
|
||||
|
||||
when(() => memoryService.getMemoryLane('user-1')).thenAnswer((_) async => []);
|
||||
when(() => userService.tryGetMyUser()).thenReturn(user());
|
||||
when(() => userService.watchMyUser()).thenAnswer((_) => const Stream.empty());
|
||||
});
|
||||
|
||||
group('driftMemoryFutureProvider', () {
|
||||
test('re-queries after local midnight', () {
|
||||
fakeAsync((async) {
|
||||
final container = makeContainer();
|
||||
container.listen(driftMemoryFutureProvider, (_, __) {});
|
||||
async.flushMicrotasks();
|
||||
|
||||
verify(() => memoryService.getMemoryLane('user-1')).called(1);
|
||||
|
||||
async.elapse(const Duration(seconds: 4));
|
||||
async.flushMicrotasks();
|
||||
verifyNever(() => memoryService.getMemoryLane('user-1'));
|
||||
|
||||
async.elapse(const Duration(hours: 25));
|
||||
async.flushMicrotasks();
|
||||
verify(() => memoryService.getMemoryLane('user-1')).called(greaterThanOrEqualTo(1));
|
||||
});
|
||||
});
|
||||
|
||||
test('cancels the midnight timer when disposed', () {
|
||||
fakeAsync((async) {
|
||||
final container = makeContainer();
|
||||
final subscription = container.listen(driftMemoryFutureProvider, (_, __) {});
|
||||
async.flushMicrotasks();
|
||||
verify(() => memoryService.getMemoryLane('user-1')).called(1);
|
||||
|
||||
subscription.close();
|
||||
async.elapse(const Duration(hours: 25));
|
||||
async.flushMicrotasks();
|
||||
|
||||
verifyNever(() => memoryService.getMemoryLane('user-1'));
|
||||
});
|
||||
});
|
||||
|
||||
test('does not query or arm the timer when memories are disabled', () {
|
||||
when(() => userService.tryGetMyUser()).thenReturn(user(memoryEnabled: false));
|
||||
|
||||
fakeAsync((async) {
|
||||
final container = makeContainer();
|
||||
container.listen(driftMemoryFutureProvider, (_, __) {});
|
||||
async.flushMicrotasks();
|
||||
|
||||
async.elapse(const Duration(hours: 25));
|
||||
async.flushMicrotasks();
|
||||
|
||||
verifyNever(() => memoryService.getMemoryLane(any()));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue