fix(mobile): 新的同事页合并发出/收到申请并按方向渲染(视觉审查 🔴1/🔴2)
- 合并 getFriendApplicationListAsApplicant(我发出的)+ AsRecipient(我收到的), 按 fromUserID 是否等于自己判断方向 - 我发出的待处理 → 灰字「等待对方通过」且无操作按钮;我收到的待处理才渲染 「接受/拒绝」;已同意→已添加、已拒绝→已拒绝(视觉规范 5.1) - 接受/拒绝按钮 shape 复用 theme.dart 新增的统一 8px Token(AppRadius.button), 不再用 Material3 默认胶囊圆角,页面内零魔法数值 - 窄屏(<360)按钮组/状态字换第二行右对齐不挤压姓名;非窄屏状态区固定 132px 双机实测:A(t2001)发→A 侧 AsApplicant 显示 handleResult=0(等待对方通过、无按钮); B(t2002)收→AsRecipient 显示接受/拒绝按钮,点接受后双方 handleResult=1(已添加) Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
|
||||
|
||||
import '../services/im_service.dart';
|
||||
import '../theme.dart';
|
||||
import '../widgets/avatar.dart';
|
||||
|
||||
/// 「新的同事」:收到的好友申请列表,可接受 / 拒绝
|
||||
/// 「新的同事」:收到的 + 我发出的好友申请列表。
|
||||
/// 按申请方向渲染(视觉规范 v1 §5.1):
|
||||
/// - 我发出的待处理 → 灰字「等待对方通过」,无操作按钮;
|
||||
/// - 我收到的待处理 → 「接受 / 拒绝」按钮组;
|
||||
/// - 已同意 → 「已添加」;已拒绝 → 「已拒绝」;失败 → toast 且行保持可重试。
|
||||
class NewFriendsScreen extends StatefulWidget {
|
||||
const NewFriendsScreen({super.key});
|
||||
|
||||
@@ -25,10 +30,23 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
|
||||
Future<void> _load() async {
|
||||
try {
|
||||
final list = await OpenIM.iMManager.friendshipManager.getFriendApplicationListAsRecipient();
|
||||
// 合并两类申请:我发出的 + 我收到的,按申请时间倒序,待处理在前
|
||||
final results = await Future.wait([
|
||||
OpenIM.iMManager.friendshipManager.getFriendApplicationListAsRecipient(),
|
||||
OpenIM.iMManager.friendshipManager.getFriendApplicationListAsApplicant(),
|
||||
]);
|
||||
final merged = <FriendApplicationInfo>[
|
||||
...(results[0] ?? <FriendApplicationInfo>[]),
|
||||
...(results[1] ?? <FriendApplicationInfo>[]),
|
||||
];
|
||||
merged.sort((a, b) {
|
||||
if ((a.handleResult ?? 0) == 0 && (b.handleResult ?? 0) != 0) return -1;
|
||||
if ((b.handleResult ?? 0) == 0 && (a.handleResult ?? 0) != 0) return 1;
|
||||
return (b.createTime ?? 0).compareTo(a.createTime ?? 0);
|
||||
});
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_list = list;
|
||||
_list = merged;
|
||||
_loading = false;
|
||||
_failed = false;
|
||||
});
|
||||
@@ -41,6 +59,7 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理收到的申请:接受 / 拒绝。成功才改状态,失败 toast 保持待处理可重试。
|
||||
Future<void> _handle(FriendApplicationInfo a, bool accept) async {
|
||||
try {
|
||||
if (accept) {
|
||||
@@ -79,7 +98,13 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
children: [
|
||||
const Text('申请列表没加载出来', style: TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary)),
|
||||
const SizedBox(height: AppGap.x4),
|
||||
OutlinedButton(onPressed: _load, child: const Text('重试')),
|
||||
OutlinedButton(
|
||||
onPressed: _load,
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.button)),
|
||||
),
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -96,10 +121,21 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 我发出的申请(fromUserID 是我自己);否则是我收到的。
|
||||
bool _isOutgoing(FriendApplicationInfo a) {
|
||||
final myID = IMService.instance.currentUserID;
|
||||
return myID != null && a.fromUserID == myID;
|
||||
}
|
||||
|
||||
Widget _tile(FriendApplicationInfo a) {
|
||||
final name = a.fromNickname?.isNotEmpty == true ? a.fromNickname! : (a.fromUserID ?? '');
|
||||
final isPending = a.handleResult == 0;
|
||||
final showButtons = isPending;
|
||||
final outgoing = _isOutgoing(a);
|
||||
// 我发出的显示对方(to),我收到的显示申请人(from)
|
||||
final name = outgoing
|
||||
? (a.toNickname?.isNotEmpty == true ? a.toNickname! : (a.toUserID ?? ''))
|
||||
: (a.fromNickname?.isNotEmpty == true ? a.fromNickname! : (a.fromUserID ?? ''));
|
||||
final isPending = (a.handleResult ?? 0) == 0;
|
||||
// 只有「我收到的待处理」才显示操作按钮;我发出的待处理只显示状态字
|
||||
final showButtons = isPending && !outgoing;
|
||||
// 窄屏(<360)时按钮组换到第二行右对齐,避免挤压姓名区
|
||||
final narrow = MediaQuery.sizeOf(context).width < 360;
|
||||
|
||||
@@ -113,6 +149,7 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
backgroundColor: AppColors.primary,
|
||||
minimumSize: const Size(60, 32),
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.button)),
|
||||
),
|
||||
child: const Text('接受', style: TextStyle(fontSize: AppFont.sub)),
|
||||
),
|
||||
@@ -122,6 +159,7 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(60, 32),
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.button)),
|
||||
),
|
||||
child: const Text('拒绝', style: TextStyle(fontSize: AppFont.sub)),
|
||||
),
|
||||
@@ -129,11 +167,12 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
);
|
||||
|
||||
Widget statusText;
|
||||
if (a.handleResult == 1) {
|
||||
if ((a.handleResult ?? 0) == 1) {
|
||||
statusText = const Text('已添加', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
||||
} else if (a.handleResult == -1) {
|
||||
} else if ((a.handleResult ?? 0) == -1) {
|
||||
statusText = const Text('已拒绝', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
||||
} else {
|
||||
// 待处理:我发出的显示「等待对方通过」,我收到的走按钮组(showButtons)
|
||||
statusText = const Text('等待对方通过', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
||||
}
|
||||
|
||||
@@ -164,15 +203,23 @@ class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
||||
style: const TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary),
|
||||
),
|
||||
],
|
||||
if (narrow && showButtons) ...[
|
||||
// 窄屏:按钮组 / 状态字换到第二行右对齐,不挤压姓名区
|
||||
if (narrow) ...[
|
||||
const SizedBox(height: AppGap.x2),
|
||||
Align(alignment: Alignment.centerRight, child: buttonGroup),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: showButtons ? buttonGroup : statusText,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
// 非窄屏:状态区固定 132px 不压缩
|
||||
if (!narrow)
|
||||
SizedBox(width: 132, child: Align(alignment: Alignment.centerRight, child: showButtons ? buttonGroup : statusText)),
|
||||
SizedBox(
|
||||
width: 132,
|
||||
child: Align(alignment: Alignment.centerRight, child: showButtons ? buttonGroup : statusText),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -75,6 +75,20 @@ class AppGap {
|
||||
static const double x12 = 48;
|
||||
}
|
||||
|
||||
/// 圆角常量(视觉规范 v1 §2:按钮/输入框/列表项 = 8,弹窗 = 12,气泡 = 8)
|
||||
class AppRadius {
|
||||
AppRadius._();
|
||||
|
||||
/// 按钮 / 输入框 / 列表项
|
||||
static const double button = 8;
|
||||
|
||||
/// 弹窗
|
||||
static const double modal = 12;
|
||||
|
||||
/// 气泡
|
||||
static const double bubble = 8;
|
||||
}
|
||||
|
||||
/// 统一的主题
|
||||
ThemeData buildAppTheme() {
|
||||
return ThemeData(
|
||||
|
||||
Reference in New Issue
Block a user