From c6fd342072c6e5f0df2a3cd1f342f0d48f5281c0 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 7 Jan 2026 01:42:20 +0800 Subject: [PATCH] :recycle: Refactored the relationship screen according to server updates --- assets/i18n/en-US.json | 13 +- assets/i18n/es-ES.json | 3 + assets/i18n/ja-JP.json | 3 + assets/i18n/ko-KR.json | 3 + assets/i18n/zh-CN.json | 3 + assets/i18n/zh-OG.json | 3 + assets/i18n/zh-TW.json | 3 + lib/models/relationship.dart | 5 +- lib/models/relationship.freezed.dart | 78 ++++++----- lib/models/relationship.g.dart | 12 +- lib/screens/account/relationship.dart | 165 +++++++++++++++--------- lib/screens/account/relationship.g.dart | 16 +-- 12 files changed, 197 insertions(+), 110 deletions(-) diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 134dc780..b265d04c 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -283,14 +283,17 @@ "relationshipStatusBlocked": "Blocked", "blockUser": "Block User", "unblockUser": "Unblock User", + "forgotRelationship": "Forgot Relationship", + "forgotRelationshipConfirm": "Are you sure you want to forget your relationship with {}?", + "relationshipDeleted": "Relationship deleted", "friendRequestAccepted": "Accepted friend request from {}", "friendRequestDeclined": "Declined friend request from {}", "requestExpiredIn": "Expired in {}", - "friendSentRequest": "Sent Friend Requests", - "friendSentRequestEmpty": "No sent friend requests", - "friendSentRequestHint": { - "one": "{} friend request sent", - "other": "{} friend requests sent" + "friendRequests": "Friend Requests", + "friendRequestsEmpty": "No related friend requests", + "friendRequestsHint": { + "one": "{} friend request", + "other": "{} friend requests" }, "levelingProgress": "Leveling Progress", "levelingProgressExperience": "{} EXP", diff --git a/assets/i18n/es-ES.json b/assets/i18n/es-ES.json index b6e6fd8b..fa248aeb 100644 --- a/assets/i18n/es-ES.json +++ b/assets/i18n/es-ES.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "Blocked", "blockUser": "Block User", "unblockUser": "Unblock User", + "forgotRelationship": "Olvidar Relación", + "forgotRelationshipConfirm": "¿Estás seguro de que quieres olvidar tu relación con {}?", + "relationshipDeleted": "Relación eliminada", "friendRequestAccepted": "Accepted friend request from {}", "friendRequestDeclined": "Declined friend request from {}", "requestExpiredIn": "Expired in {}", diff --git a/assets/i18n/ja-JP.json b/assets/i18n/ja-JP.json index b6e6fd8b..47c383cb 100644 --- a/assets/i18n/ja-JP.json +++ b/assets/i18n/ja-JP.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "Blocked", "blockUser": "Block User", "unblockUser": "Unblock User", + "forgotRelationship": "関係を忘れる", + "forgotRelationshipConfirm": "{} との関係を忘れてもよろしいですか?", + "relationshipDeleted": "関係が削除されました", "friendRequestAccepted": "Accepted friend request from {}", "friendRequestDeclined": "Declined friend request from {}", "requestExpiredIn": "Expired in {}", diff --git a/assets/i18n/ko-KR.json b/assets/i18n/ko-KR.json index b6e6fd8b..3af8ef58 100644 --- a/assets/i18n/ko-KR.json +++ b/assets/i18n/ko-KR.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "Blocked", "blockUser": "Block User", "unblockUser": "Unblock User", + "forgotRelationship": "관계 잊기", + "forgotRelationshipConfirm": "{}와의 관계를 잊으시겠습니까?", + "relationshipDeleted": "관계가 삭제되었습니다", "friendRequestAccepted": "Accepted friend request from {}", "friendRequestDeclined": "Declined friend request from {}", "requestExpiredIn": "Expired in {}", diff --git a/assets/i18n/zh-CN.json b/assets/i18n/zh-CN.json index 4d6d04d2..2d9309b8 100644 --- a/assets/i18n/zh-CN.json +++ b/assets/i18n/zh-CN.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "已屏蔽", "blockUser": "屏蔽用户", "unblockUser": "解除屏蔽用户", + "forgotRelationship": "忘记关系", + "forgotRelationshipConfirm": "确定要忘记与 {} 的关系吗?", + "relationshipDeleted": "关系已删除", "friendRequestAccepted": "已接受 {} 的好友请求", "friendRequestDeclined": "已拒绝 {} 的好友请求", "requestExpiredIn": "{} 后过期", diff --git a/assets/i18n/zh-OG.json b/assets/i18n/zh-OG.json index ed320c61..21413203 100644 --- a/assets/i18n/zh-OG.json +++ b/assets/i18n/zh-OG.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "已阻", "blockUser": "禁此戶", "unblockUser": "解禁", + "forgotRelationship": "忘締交", + "forgotRelationshipConfirm": "確定要忘與 {} 之締交乎?", + "relationshipDeleted": "締交已忘", "friendRequestAccepted": "已納 {} 締交書", "friendRequestDeclined": "已謝 {} 締交書", "requestExpiredIn": "效期 {}", diff --git a/assets/i18n/zh-TW.json b/assets/i18n/zh-TW.json index 0d6a5035..f9a0c5ba 100644 --- a/assets/i18n/zh-TW.json +++ b/assets/i18n/zh-TW.json @@ -265,6 +265,9 @@ "relationshipStatusBlocked": "已屏蔽", "blockUser": "屏蔽用戶", "unblockUser": "解除屏蔽用戶", + "forgotRelationship": "忘記關係", + "forgotRelationshipConfirm": "確定要忘記與 {} 的關係嗎?", + "relationshipDeleted": "關係已刪除", "friendRequestAccepted": "已接受 {} 的好友請求", "friendRequestDeclined": "已拒絕 {} 的好友請求", "requestExpiredIn": "{} 後過期", diff --git a/lib/models/relationship.dart b/lib/models/relationship.dart index 10198ba8..8f04bea4 100644 --- a/lib/models/relationship.dart +++ b/lib/models/relationship.dart @@ -12,9 +12,10 @@ sealed class SnRelationship with _$SnRelationship { required DateTime? updatedAt, required DateTime? deletedAt, required String accountId, - required SnAccount account, + // Usually the account was not included in the response + required SnAccount? account, required String relatedId, - required SnAccount related, + required SnAccount? related, required DateTime? expiredAt, required int status, }) = _SnRelationship; diff --git a/lib/models/relationship.freezed.dart b/lib/models/relationship.freezed.dart index ef04deea..d2b28891 100644 --- a/lib/models/relationship.freezed.dart +++ b/lib/models/relationship.freezed.dart @@ -15,7 +15,8 @@ T _$identity(T value) => value; /// @nodoc mixin _$SnRelationship { - DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String get accountId; SnAccount get account; String get relatedId; SnAccount get related; DateTime? get expiredAt; int get status; + DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String get accountId;// Usually the account was not included in the response + SnAccount? get account; String get relatedId; SnAccount? get related; DateTime? get expiredAt; int get status; /// Create a copy of SnRelationship /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -48,11 +49,11 @@ abstract mixin class $SnRelationshipCopyWith<$Res> { factory $SnRelationshipCopyWith(SnRelationship value, $Res Function(SnRelationship) _then) = _$SnRelationshipCopyWithImpl; @useResult $Res call({ - DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status + DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status }); -$SnAccountCopyWith<$Res> get account;$SnAccountCopyWith<$Res> get related; +$SnAccountCopyWith<$Res>? get account;$SnAccountCopyWith<$Res>? get related; } /// @nodoc @@ -65,16 +66,16 @@ class _$SnRelationshipCopyWithImpl<$Res> /// Create a copy of SnRelationship /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = null,Object? relatedId = null,Object? related = null,Object? expiredAt = freezed,Object? status = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = freezed,Object? relatedId = null,Object? related = freezed,Object? expiredAt = freezed,Object? status = null,}) { return _then(_self.copyWith( createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable -as String,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable -as SnAccount,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable -as String,related: null == related ? _self.related : related // ignore: cast_nullable_to_non_nullable -as SnAccount,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable +as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount?,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable +as String,related: freezed == related ? _self.related : related // ignore: cast_nullable_to_non_nullable +as SnAccount?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable as DateTime?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as int, )); @@ -83,18 +84,24 @@ as int, /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res> get account { - - return $SnAccountCopyWith<$Res>(_self.account, (value) { +$SnAccountCopyWith<$Res>? get account { + if (_self.account == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.account!, (value) { return _then(_self.copyWith(account: value)); }); }/// Create a copy of SnRelationship /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res> get related { - - return $SnAccountCopyWith<$Res>(_self.related, (value) { +$SnAccountCopyWith<$Res>? get related { + if (_self.related == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.related!, (value) { return _then(_self.copyWith(related: value)); }); } @@ -176,7 +183,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SnRelationship() when $default != null: return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);case _: @@ -197,7 +204,7 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId, /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status) $default,) {final _that = this; switch (_that) { case _SnRelationship(): return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);} @@ -214,7 +221,7 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId, /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status)? $default,) {final _that = this; switch (_that) { case _SnRelationship() when $default != null: return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);case _: @@ -236,9 +243,10 @@ class _SnRelationship implements SnRelationship { @override final DateTime? updatedAt; @override final DateTime? deletedAt; @override final String accountId; -@override final SnAccount account; +// Usually the account was not included in the response +@override final SnAccount? account; @override final String relatedId; -@override final SnAccount related; +@override final SnAccount? related; @override final DateTime? expiredAt; @override final int status; @@ -275,11 +283,11 @@ abstract mixin class _$SnRelationshipCopyWith<$Res> implements $SnRelationshipCo factory _$SnRelationshipCopyWith(_SnRelationship value, $Res Function(_SnRelationship) _then) = __$SnRelationshipCopyWithImpl; @override @useResult $Res call({ - DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status + DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status }); -@override $SnAccountCopyWith<$Res> get account;@override $SnAccountCopyWith<$Res> get related; +@override $SnAccountCopyWith<$Res>? get account;@override $SnAccountCopyWith<$Res>? get related; } /// @nodoc @@ -292,16 +300,16 @@ class __$SnRelationshipCopyWithImpl<$Res> /// Create a copy of SnRelationship /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = null,Object? relatedId = null,Object? related = null,Object? expiredAt = freezed,Object? status = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = freezed,Object? relatedId = null,Object? related = freezed,Object? expiredAt = freezed,Object? status = null,}) { return _then(_SnRelationship( createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable -as String,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable -as SnAccount,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable -as String,related: null == related ? _self.related : related // ignore: cast_nullable_to_non_nullable -as SnAccount,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable +as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount?,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable +as String,related: freezed == related ? _self.related : related // ignore: cast_nullable_to_non_nullable +as SnAccount?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable as DateTime?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as int, )); @@ -311,18 +319,24 @@ as int, /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res> get account { - - return $SnAccountCopyWith<$Res>(_self.account, (value) { +$SnAccountCopyWith<$Res>? get account { + if (_self.account == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.account!, (value) { return _then(_self.copyWith(account: value)); }); }/// Create a copy of SnRelationship /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res> get related { - - return $SnAccountCopyWith<$Res>(_self.related, (value) { +$SnAccountCopyWith<$Res>? get related { + if (_self.related == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.related!, (value) { return _then(_self.copyWith(related: value)); }); } diff --git a/lib/models/relationship.g.dart b/lib/models/relationship.g.dart index 4679a8d0..1c367faf 100644 --- a/lib/models/relationship.g.dart +++ b/lib/models/relationship.g.dart @@ -18,9 +18,13 @@ _SnRelationship _$SnRelationshipFromJson(Map json) => ? null : DateTime.parse(json['deleted_at'] as String), accountId: json['account_id'] as String, - account: SnAccount.fromJson(json['account'] as Map), + account: json['account'] == null + ? null + : SnAccount.fromJson(json['account'] as Map), relatedId: json['related_id'] as String, - related: SnAccount.fromJson(json['related'] as Map), + related: json['related'] == null + ? null + : SnAccount.fromJson(json['related'] as Map), expiredAt: json['expired_at'] == null ? null : DateTime.parse(json['expired_at'] as String), @@ -33,9 +37,9 @@ Map _$SnRelationshipToJson(_SnRelationship instance) => 'updated_at': instance.updatedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), 'account_id': instance.accountId, - 'account': instance.account.toJson(), + 'account': instance.account?.toJson(), 'related_id': instance.relatedId, - 'related': instance.related.toJson(), + 'related': instance.related?.toJson(), 'expired_at': instance.expiredAt?.toIso8601String(), 'status': instance.status, }; diff --git a/lib/screens/account/relationship.dart b/lib/screens/account/relationship.dart index 36ce629d..ef2cdc49 100644 --- a/lib/screens/account/relationship.dart +++ b/lib/screens/account/relationship.dart @@ -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> sentFriendRequest(Ref ref) async { .toList(); } -final relationshipListNotifierProvider = - AsyncNotifierProvider.autoDispose< - RelationshipListNotifier, - PaginationState - >(RelationshipListNotifier.new); +final relationshipListNotifierProvider = AsyncNotifierProvider.autoDispose( + RelationshipListNotifier.new, +); class RelationshipListNotifier extends AsyncNotifier> @@ -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 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 updateRelationship( SnRelationship relationship, int newStatus, @@ -276,8 +263,32 @@ class RelationshipScreen extends HookConsumerWidget { relationshipNotifier.refresh(); } + Future 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 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 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, ); diff --git a/lib/screens/account/relationship.g.dart b/lib/screens/account/relationship.g.dart index e469601d..02eed05a 100644 --- a/lib/screens/account/relationship.g.dart +++ b/lib/screens/account/relationship.g.dart @@ -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>, @@ -22,19 +22,19 @@ final class SentFriendRequestProvider with $FutureModifier>, $FutureProvider> { - 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> create(Ref ref) { - return sentFriendRequest(ref); + return friendRequest(ref); } } -String _$sentFriendRequestHash() => r'0c52813eb6f86c05f6e0b1e4e840d0d9c350aa9e'; +String _$friendRequestHash() => r'b066553f9bd0505a7a272cf10cb3ca312152acd6';