Files
immich/mobile/lib/presentation/actions/partner.action.dart
T
2026-07-30 01:29:44 +05:30

127 lines
4.3 KiB
Dart

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/utils/error_handler.dart';
import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
class PartnerAddAction extends ActionBuilder {
const PartnerAddAction();
@override
ActionItem create(BuildContext context, WidgetRef ref) =>
ActionItem(icon: Icons.person_add_rounded, label: context.t.add_partner, onAction: () => _add(context, ref));
Future<void> _add(BuildContext context, WidgetRef ref) async {
final partnerService = ref.read(partnerServiceProvider);
final authUserId = ref.read(authUserProvider).id;
final selected = await showDialog<User>(context: context, builder: (_) => const PartnerSelectionDialog());
if (selected == null) {
return;
}
try {
await partnerService.create(sharedById: authUserId, sharedWithId: selected.id);
} catch (error, stack) {
handleError(error, stack: stack, description: 'Failed to add partner');
}
}
}
class PartnerRemoveAction extends ActionBuilder {
const PartnerRemoveAction({required this.sharedWithId, required this.partnerName});
final String sharedWithId;
final String partnerName;
@override
ActionItem create(BuildContext context, WidgetRef ref) =>
ActionItem(icon: Icons.person_remove_rounded, label: context.t.remove, onAction: () => _remove(context, ref));
Future<void> _remove(BuildContext context, WidgetRef ref) async {
final partnerService = ref.read(partnerServiceProvider);
final authUserId = ref.read(authUserProvider).id;
final confirmed = await showDialog<bool>(
context: context,
builder: (_) => ConfirmDialog(
title: context.t.stop_photo_sharing,
content: context.t.partner_page_stop_sharing_content(partner: partnerName),
),
);
if (confirmed != true) {
return;
}
try {
await partnerService.delete(sharedById: authUserId, sharedWithId: sharedWithId);
} catch (error, stack) {
handleError(error, stack: stack, description: 'Failed to remove partner');
}
}
}
@visibleForTesting
final candidatesStateProvider = StreamProvider.autoDispose<Iterable<User>>(
(ref) => ref.watch(partnerServiceProvider).getCandidates(ref.watch(authUserProvider).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)),
),
],
),
);
}
}