import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_mobile/presentation/actions/action.dart'; import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; class PartnerAddAction extends BaseAction { const PartnerAddAction(); @override IconData get icon => Icons.person_add_rounded; @override String label(ActionScope scope) => scope.context.t.add_partner; @override Future onAction(ActionScope scope) async { final ActionScope(:context, :ref, :authUser) = scope; final selected = await showDialog(context: context, builder: (_) => const PartnerSelectionDialog()); if (selected == null) { return; } await ref.read(partnerServiceProvider).create(sharedById: authUser.id, sharedWithId: selected.id); } } class PartnerRemoveAction extends BaseAction { const PartnerRemoveAction({required this.sharedWithId, required this.partnerName}); final String sharedWithId; final String partnerName; @override IconData get icon => Icons.person_remove_rounded; @override String label(ActionScope scope) => scope.context.t.remove; @override Future onAction(ActionScope scope) async { final ActionScope(:context, :ref, :authUser) = scope; final confirmed = await showDialog( context: context, builder: (_) => ConfirmDialog( title: context.t.stop_photo_sharing, content: context.t.partner_page_stop_sharing_content(partner: partnerName), ), ); if (confirmed != true) { return; } await ref.read(partnerServiceProvider).delete(sharedById: authUser.id, sharedWithId: sharedWithId); } } @visibleForTesting final candidatesStateProvider = StreamProvider.autoDispose>((ref) { final currentUser = ref.watch(currentUserProvider); // TODO: Refactor with a route guard to avoid this check in every provider if (currentUser == null) { return const Stream.empty(); } return ref.watch(partnerServiceProvider).getCandidates(currentUser.id); }); @visibleForTesting class PartnerSelectionDialog extends ConsumerWidget { const PartnerSelectionDialog({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final candidatesAsync = ref.watch(candidatesStateProvider); return SimpleDialog( title: Text(context.t.partner_page_select_partner), children: candidatesAsync.when( data: (candidates) { final users = candidates.toList(); if (users.isEmpty) { return [ Padding( padding: const .symmetric(horizontal: 24, vertical: 8), child: Text(context.t.partner_page_no_more_users), ), ]; } return [ for (final candidate in users) SimpleDialogOption( onPressed: () => Navigator.of(context).pop(candidate), child: Row( children: [ Padding( padding: const .only(right: 8), child: PartnerUserAvatar(userId: candidate.id, name: candidate.name), ), Text(candidate.name), ], ), ), ]; }, loading: () => const [ Padding( padding: .all(24), child: Center(child: CircularProgressIndicator()), ), ], error: (error, _) => [ Padding( padding: const .symmetric(horizontal: 24, vertical: 8), child: Text(context.t.error_loading_partners(error: error)), ), ], ), ); } }