♻️ Refactored the relationship screen according to server updates

This commit is contained in:
2026-01-07 01:42:20 +08:00
parent 757f1b880c
commit c6fd342072
12 changed files with 197 additions and 110 deletions

View File

@@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/paging.dart';
import 'package:island/pods/userinfo.dart';
@@ -31,11 +32,9 @@ Future<List<SnRelationship>> sentFriendRequest(Ref ref) async {
.toList();
}
final relationshipListNotifierProvider =
AsyncNotifierProvider.autoDispose<
RelationshipListNotifier,
PaginationState<SnRelationship>
>(RelationshipListNotifier.new);
final relationshipListNotifierProvider = AsyncNotifierProvider.autoDispose(
RelationshipListNotifier.new,
);
class RelationshipListNotifier
extends AsyncNotifier<PaginationState<SnRelationship>>
@@ -84,6 +83,7 @@ class RelationshipListTile extends StatelessWidget {
final String? currentUserId;
final bool showRelatedAccount;
final Function(SnRelationship, int)? onUpdateStatus;
final Function(SnRelationship)? onDelete;
const RelationshipListTile({
super.key,
@@ -96,18 +96,19 @@ class RelationshipListTile extends StatelessWidget {
required this.currentUserId,
this.showRelatedAccount = false,
this.onUpdateStatus,
this.onDelete,
});
@override
Widget build(BuildContext context) {
final account = showRelatedAccount
? relationship.related
: relationship.account;
? relationship.related!
: relationship.account!;
final isPending =
relationship.status == 0 && relationship.relatedId == currentUserId;
final isWaiting =
relationship.status == 0 && relationship.accountId == currentUserId;
final isEstablished = relationship.status == 1 || relationship.status == 2;
final isEstablished = relationship.status != 0;
return ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 12),
@@ -185,21 +186,36 @@ class RelationshipListTile extends StatelessWidget {
itemBuilder: (context) => [
if (relationship.status >= 100) // If friend
PopupMenuItem(
child: ListTile(
leading: const Icon(Symbols.block),
title: Text('blockUser').tr(),
contentPadding: EdgeInsets.zero,
),
onTap: () => onUpdateStatus?.call(relationship, -100),
child: Row(
children: [
const Icon(Symbols.block),
const Gap(12),
Text('blockUser').tr(),
],
),
)
else if (relationship.status <= -100) // If blocked
PopupMenuItem(
child: ListTile(
leading: const Icon(Symbols.person_add),
title: Text('unblockUser').tr(),
contentPadding: EdgeInsets.zero,
),
onTap: () => onUpdateStatus?.call(relationship, 100),
child: Row(
children: [
const Icon(Symbols.person_add),
const Gap(12),
Text('unblockUser').tr(),
],
),
),
if (onDelete != null)
PopupMenuItem(
onTap: () => onDelete?.call(relationship),
child: Row(
children: [
const Icon(Symbols.delete),
const Gap(12),
Text('forgotRelationship').tr(),
],
),
),
],
),
@@ -230,40 +246,11 @@ class RelationshipScreen extends HookConsumerWidget {
final client = ref.read(apiClientProvider);
await client.post('/pass/relationships/${result.id}/friends');
ref.invalidate(sentFriendRequestProvider);
ref.invalidate(friendRequestProvider);
}
final submitting = useState(false);
Future<void> handleFriendRequest(
SnRelationship relationship,
bool isAccept,
) async {
try {
submitting.value = true;
final client = ref.read(apiClientProvider);
await client.post(
'/pass/relationships/${relationship.accountId}/friends/${isAccept ? 'accept' : 'decline'}',
);
relationshipNotifier.refresh();
if (!context.mounted) return;
if (isAccept) {
showSnackBar(
'friendRequestAccepted'.tr(args: ['@${relationship.account.name}']),
);
} else {
showSnackBar(
'friendRequestDeclined'.tr(args: ['@${relationship.account.name}']),
);
}
HapticFeedback.lightImpact();
} catch (err) {
showErrorAlert(err);
} finally {
submitting.value = false;
}
}
Future<void> updateRelationship(
SnRelationship relationship,
int newStatus,
@@ -276,8 +263,32 @@ class RelationshipScreen extends HookConsumerWidget {
relationshipNotifier.refresh();
}
Future<void> deleteRelationship(SnRelationship relationship) async {
final confirmed = await showConfirmAlert(
'forgotRelationshipConfirm'.tr(
args: ['@${relationship.related!.name}'],
),
'forgotRelationship'.tr(),
isDanger: true,
);
if (!confirmed) return;
if (!context.mounted) return;
showLoadingModal(context);
try {
final client = ref.read(apiClientProvider);
await client.delete('/pass/relationships/${relationship.accountId}');
relationshipNotifier.refresh();
showSnackBar('relationshipDeleted'.tr());
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
final user = ref.watch(userInfoProvider);
final requests = ref.watch(sentFriendRequestProvider);
final requests = ref.watch(friendRequestProvider);
return AppScaffold(
appBar: AppBar(title: Text('relationships').tr()),
@@ -293,9 +304,9 @@ class RelationshipScreen extends HookConsumerWidget {
if (requests.hasValue && requests.value!.isNotEmpty)
ListTile(
leading: const Icon(Symbols.send),
title: Text('friendSentRequest').tr(),
title: Text('friendRequests').tr(),
subtitle: Text(
'friendSentRequestHint'.plural(requests.value!.length),
'friendRequestsHint'.plural(requests.value!.length),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
onTap: () {
@@ -316,11 +327,10 @@ class RelationshipScreen extends HookConsumerWidget {
return RelationshipListTile(
relationship: relationship,
submitting: submitting.value,
onAccept: () => handleFriendRequest(relationship, true),
onDecline: () => handleFriendRequest(relationship, false),
currentUserId: user.value?.id,
showRelatedAccount: false,
showRelatedAccount: true,
onUpdateStatus: updateRelationship,
onDelete: deleteRelationship,
);
},
),
@@ -336,19 +346,54 @@ class _SentFriendRequestsSheet extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final requests = ref.watch(sentFriendRequestProvider);
final requests = ref.watch(friendRequestProvider);
final user = ref.watch(userInfoProvider);
Future<void> cancelRequest(SnRelationship request) async {
try {
final client = ref.read(apiClientProvider);
await client.delete('/pass/relationships/${request.relatedId}/friends');
ref.invalidate(sentFriendRequestProvider);
ref.invalidate(friendRequestProvider);
} catch (err) {
showErrorAlert(err);
}
}
final submitting = useState(false);
Future<void> handleFriendRequest(
SnRelationship relationship,
bool isAccept,
) async {
try {
submitting.value = true;
final client = ref.read(apiClientProvider);
await client.post(
'/pass/relationships/${relationship.accountId}/friends/${isAccept ? 'accept' : 'decline'}',
);
ref.invalidate(friendRequestProvider);
if (!context.mounted) return;
if (isAccept) {
showSnackBar(
'friendRequestAccepted'.tr(
args: ['@${relationship.account!.name}'],
),
);
} else {
showSnackBar(
'friendRequestDeclined'.tr(
args: ['@${relationship.account!.name}'],
),
);
}
HapticFeedback.lightImpact();
} catch (err) {
showErrorAlert(err);
} finally {
submitting.value = false;
}
}
return Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.8,
@@ -366,7 +411,7 @@ class _SentFriendRequestsSheet extends HookConsumerWidget {
child: Row(
children: [
Text(
'friendSentRequest'.tr(),
'friendRequests'.tr(),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: -0.5,
@@ -377,7 +422,7 @@ class _SentFriendRequestsSheet extends HookConsumerWidget {
icon: const Icon(Symbols.refresh),
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
onPressed: () {
ref.invalidate(sentFriendRequestProvider);
ref.invalidate(friendRequestProvider);
},
),
IconButton(
@@ -394,7 +439,7 @@ class _SentFriendRequestsSheet extends HookConsumerWidget {
data: (items) => items.isEmpty
? Center(
child: Text(
'friendSentRequestEmpty'.tr(),
'friendRequestsEmpty'.tr(),
textAlign: TextAlign.center,
),
)
@@ -406,6 +451,8 @@ class _SentFriendRequestsSheet extends HookConsumerWidget {
return RelationshipListTile(
relationship: request,
onCancel: () => cancelRequest(request),
onAccept: () => handleFriendRequest(request, true),
onDecline: () => handleFriendRequest(request, false),
currentUserId: user.value?.id,
showRelatedAccount: true,
);

View File

@@ -9,10 +9,10 @@ part of 'relationship.dart';
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(sentFriendRequest)
final sentFriendRequestProvider = SentFriendRequestProvider._();
@ProviderFor(friendRequest)
final friendRequestProvider = FriendRequestProvider._();
final class SentFriendRequestProvider
final class FriendRequestProvider
extends
$FunctionalProvider<
AsyncValue<List<SnRelationship>>,
@@ -22,19 +22,19 @@ final class SentFriendRequestProvider
with
$FutureModifier<List<SnRelationship>>,
$FutureProvider<List<SnRelationship>> {
SentFriendRequestProvider._()
FriendRequestProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'sentFriendRequestProvider',
name: r'friendRequestProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$sentFriendRequestHash();
String debugGetCreateSourceHash() => _$friendRequestHash();
@$internal
@override
@@ -44,8 +44,8 @@ final class SentFriendRequestProvider
@override
FutureOr<List<SnRelationship>> create(Ref ref) {
return sentFriendRequest(ref);
return friendRequest(ref);
}
}
String _$sentFriendRequestHash() => r'0c52813eb6f86c05f6e0b1e4e840d0d9c350aa9e';
String _$friendRequestHash() => r'b066553f9bd0505a7a272cf10cb3ca312152acd6';