From 8fbd65048321d3b618a6a42fe65d406dbc49ed4a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 26 Feb 2025 17:04:43 -0600 Subject: [PATCH] refactor(mobile): refactor user provider (#16358) --- mobile/analysis_options.yaml | 4 ++-- mobile/lib/interfaces/user.interface.dart | 4 ++++ mobile/lib/pages/photos/photos.page.dart | 2 +- mobile/lib/providers/partner.provider.dart | 18 ++++++++++++---- mobile/lib/providers/timeline.provider.dart | 6 +++++- mobile/lib/providers/user.provider.dart | 22 +++++++------------- mobile/lib/repositories/user.repository.dart | 22 ++++++++++++++++++++ mobile/lib/services/user.service.dart | 10 +++++++++ 8 files changed, 65 insertions(+), 23 deletions(-) diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 6794f39b818..dd081be64e6 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -79,14 +79,14 @@ custom_lint: - lib/widgets/asset_grid/asset_grid_data_structure.dart - test/**.dart # refactor the remaining providers - - lib/providers/{db,user}.provider.dart + - lib/providers/db.provider.dart - lib/providers/backup/backup.provider.dart - import_rule_openapi: message: openapi must only be used through ApiRepositories restrict: package:openapi allowed: - # requried / wanted + # required / wanted - lib/repositories/*_api.repository.dart # acceptable exceptions for the time being - lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities diff --git a/mobile/lib/interfaces/user.interface.dart b/mobile/lib/interfaces/user.interface.dart index d099e0e50b0..17918ac1701 100644 --- a/mobile/lib/interfaces/user.interface.dart +++ b/mobile/lib/interfaces/user.interface.dart @@ -22,6 +22,10 @@ abstract interface class IUserRepository implements IDatabaseRepository { Future me(); Future clearTable(); + + Future> getTimelineUserIds(int id); + + Stream> watchTimelineUsers(int id); } enum UserSort { id } diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 7910d45e138..b3bfa366f2f 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -110,7 +110,7 @@ class PhotosPage extends HookConsumerWidget { : const SizedBox(), renderListProvider: timelineUsers.length > 1 ? multiUsersTimelineProvider(timelineUsers) - : singleUserTimelineProvider(currentUser!.isarId), + : singleUserTimelineProvider(currentUser?.isarId), buildLoadingIndicator: buildLoadingIndicator, onRefresh: refreshAssets, stackEnabled: true, diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index 38b449e96a9..282e779432d 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/entities/user.entity.dart'; class PartnerSharedWithNotifier extends StateNotifier> { final PartnerService _partnerService; + late final StreamSubscription> streamSub; PartnerSharedWithNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; @@ -16,7 +17,7 @@ class PartnerSharedWithNotifier extends StateNotifier> { state = partners; } }).then((_) { - _partnerService.watchSharedWith().listen((partners) { + streamSub = _partnerService.watchSharedWith().listen((partners) { if (!eq(state, partners)) { state = partners; } @@ -27,6 +28,14 @@ class PartnerSharedWithNotifier extends StateNotifier> { Future updatePartner(User partner, {required bool inTimeline}) { return _partnerService.updatePartner(partner, inTimeline: inTimeline); } + + @override + void dispose() { + if (mounted) { + streamSub.cancel(); + } + super.dispose(); + } } final partnerSharedWithProvider = @@ -38,6 +47,7 @@ final partnerSharedWithProvider = class PartnerSharedByNotifier extends StateNotifier> { final PartnerService _partnerService; + late final StreamSubscription> streamSub; PartnerSharedByNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; @@ -54,11 +64,11 @@ class PartnerSharedByNotifier extends StateNotifier> { }); } - late final StreamSubscription> streamSub; - @override void dispose() { - streamSub.cancel(); + if (mounted) { + streamSub.cancel(); + } super.dispose(); } } diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index b0e9482b81b..97d5698c4ce 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -5,8 +5,12 @@ import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -final singleUserTimelineProvider = StreamProvider.family( +final singleUserTimelineProvider = StreamProvider.family( (ref, userId) { + if (userId == null) { + return const Stream.empty(); + } + ref.watch(localeProvider); final timelineService = ref.watch(timelineServiceProvider); return timelineService.watchHomeTimeline(userId); diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart index c69245ea985..c143086a156 100644 --- a/mobile/lib/providers/user.provider.dart +++ b/mobile/lib/providers/user.provider.dart @@ -5,9 +5,8 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/services/user.service.dart'; class CurrentUserProvider extends StateNotifier { CurrentUserProvider(this._apiService) : super(null) { @@ -47,18 +46,14 @@ final currentUserProvider = }); class TimelineUserIdsProvider extends StateNotifier> { - TimelineUserIdsProvider(Isar db, User? currentUser) : super([]) { - final query = db.users - .filter() - .inTimelineEqualTo(true) - .or() - .isarIdEqualTo(currentUser?.isarId ?? Isar.autoIncrement) - .isarIdProperty(); - query.findAll().then((users) => state = users); - streamSub = query.watch().listen((users) => state = users); + TimelineUserIdsProvider(this._userService) : super([]) { + _userService.getTimelineUserIds().then((users) => state = users); + streamSub = + _userService.watchTimelineUserIds().listen((users) => state = users); } late final StreamSubscription> streamSub; + final UserService _userService; @override void dispose() { @@ -69,8 +64,5 @@ class TimelineUserIdsProvider extends StateNotifier> { final timelineUsersIdsProvider = StateNotifierProvider>((ref) { - return TimelineUserIdsProvider( - ref.watch(dbProvider), - ref.watch(currentUserProvider), - ); + return TimelineUserIdsProvider(ref.watch(userServiceProvider)); }); diff --git a/mobile/lib/repositories/user.repository.dart b/mobile/lib/repositories/user.repository.dart index ea67b30e0d7..190fb780c8f 100644 --- a/mobile/lib/repositories/user.repository.dart +++ b/mobile/lib/repositories/user.repository.dart @@ -70,4 +70,26 @@ class UserRepository extends DatabaseRepository implements IUserRepository { await db.users.clear(); }); } + + @override + Future> getTimelineUserIds(int id) { + return db.users + .filter() + .inTimelineEqualTo(true) + .or() + .isarIdEqualTo(id) + .isarIdProperty() + .findAll(); + } + + @override + Stream> watchTimelineUsers(int id) { + return db.users + .filter() + .inTimelineEqualTo(true) + .or() + .isarIdEqualTo(id) + .isarIdProperty() + .watch(); + } } diff --git a/mobile/lib/services/user.service.dart b/mobile/lib/services/user.service.dart index 935a751e2a0..a14b1c08f2f 100644 --- a/mobile/lib/services/user.service.dart +++ b/mobile/lib/services/user.service.dart @@ -107,4 +107,14 @@ class UserService { Future clearTable() { return _userRepository.clearTable(); } + + Future> getTimelineUserIds() async { + final me = await _userRepository.me(); + return _userRepository.getTimelineUserIds(me.isarId); + } + + Stream> watchTimelineUserIds() async* { + final me = await _userRepository.me(); + yield* _userRepository.watchTimelineUsers(me.isarId); + } }