- message_send_service:所有消息走统一门禁(登录/连接/空接收方/好友校验), 失败打印 SDK 原始 code 并映射中文提示,不再只吞异常 - chat_screen 接入统一发送服务,失败提示真实原因 - account-service 新增 GET /api/directory(登录用户可读启用员工,用于添加同事) - add_friend_screen 增加可编辑验证消息(视觉规范 4.2) - new_friends_screen 五态 + 窄屏(<360)按钮组换行不挤压姓名(视觉规范 5.3) Co-authored-by: multica-agent <github@multica.ai>
181 lines
6.0 KiB
Dart
181 lines
6.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
|
|
|
|
import '../theme.dart';
|
|
import '../widgets/avatar.dart';
|
|
|
|
/// 「新的同事」:收到的好友申请列表,可接受 / 拒绝
|
|
class NewFriendsScreen extends StatefulWidget {
|
|
const NewFriendsScreen({super.key});
|
|
|
|
@override
|
|
State<NewFriendsScreen> createState() => _NewFriendsScreenState();
|
|
}
|
|
|
|
class _NewFriendsScreenState extends State<NewFriendsScreen> {
|
|
List<FriendApplicationInfo> _list = [];
|
|
bool _loading = true;
|
|
bool _failed = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_load();
|
|
}
|
|
|
|
Future<void> _load() async {
|
|
try {
|
|
final list = await OpenIM.iMManager.friendshipManager.getFriendApplicationListAsRecipient();
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_list = list;
|
|
_loading = false;
|
|
_failed = false;
|
|
});
|
|
} catch (_) {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_loading = false;
|
|
_failed = true;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _handle(FriendApplicationInfo a, bool accept) async {
|
|
try {
|
|
if (accept) {
|
|
await OpenIM.iMManager.friendshipManager.acceptFriendApplication(userID: a.fromUserID ?? '', handleMsg: '');
|
|
} else {
|
|
await OpenIM.iMManager.friendshipManager.refuseFriendApplication(userID: a.fromUserID ?? '', handleMsg: '');
|
|
}
|
|
if (!mounted) return;
|
|
setState(() => a.handleResult = accept ? 1 : -1);
|
|
_toast(accept ? '已添加为同事' : '已拒绝');
|
|
} catch (_) {
|
|
_toast('操作失败,请检查网络后重试');
|
|
}
|
|
}
|
|
|
|
void _toast(String text) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(text), duration: const Duration(seconds: 2)));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('新的同事')),
|
|
body: _buildBody(),
|
|
);
|
|
}
|
|
|
|
Widget _buildBody() {
|
|
if (_loading) {
|
|
return const Center(child: CircularProgressIndicator(color: AppColors.primary));
|
|
}
|
|
if (_failed) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text('申请列表没加载出来', style: TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary)),
|
|
const SizedBox(height: AppGap.x4),
|
|
OutlinedButton(onPressed: _load, child: const Text('重试')),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
if (_list.isEmpty) {
|
|
return const Center(
|
|
child: Text('暂时没有新的申请', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
|
);
|
|
}
|
|
return ListView.separated(
|
|
itemCount: _list.length,
|
|
separatorBuilder: (_, __) => const Divider(indent: 76),
|
|
itemBuilder: (_, i) => _tile(_list[i]),
|
|
);
|
|
}
|
|
|
|
Widget _tile(FriendApplicationInfo a) {
|
|
final name = a.fromNickname?.isNotEmpty == true ? a.fromNickname! : (a.fromUserID ?? '');
|
|
final isPending = a.handleResult == 0;
|
|
final showButtons = isPending;
|
|
// 窄屏(<360)时按钮组换到第二行右对齐,避免挤压姓名区
|
|
final narrow = MediaQuery.sizeOf(context).width < 360;
|
|
|
|
// 「接受/拒绝」按钮组:总宽固定 132,任何情况下不压缩、不换行
|
|
final buttonGroup = Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
FilledButton(
|
|
onPressed: () => _handle(a, true),
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: AppColors.primary,
|
|
minimumSize: const Size(60, 32),
|
|
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3),
|
|
),
|
|
child: const Text('接受', style: TextStyle(fontSize: AppFont.sub)),
|
|
),
|
|
const SizedBox(width: AppGap.x2),
|
|
OutlinedButton(
|
|
onPressed: () => _handle(a, false),
|
|
style: OutlinedButton.styleFrom(
|
|
minimumSize: const Size(60, 32),
|
|
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3),
|
|
),
|
|
child: const Text('拒绝', style: TextStyle(fontSize: AppFont.sub)),
|
|
),
|
|
],
|
|
);
|
|
|
|
Widget statusText;
|
|
if (a.handleResult == 1) {
|
|
statusText = const Text('已添加', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
|
} else if (a.handleResult == -1) {
|
|
statusText = const Text('已拒绝', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
|
} else {
|
|
statusText = const Text('等待对方通过', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary));
|
|
}
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: AppGap.x4, vertical: AppGap.x3),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
NameAvatar(name: name),
|
|
const SizedBox(width: AppGap.x3),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
name,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary),
|
|
),
|
|
if (a.reqMsg?.isNotEmpty == true) ...[
|
|
const SizedBox(height: AppGap.x1),
|
|
Text(
|
|
a.reqMsg!,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary),
|
|
),
|
|
],
|
|
if (narrow && showButtons) ...[
|
|
const SizedBox(height: AppGap.x2),
|
|
Align(alignment: Alignment.centerRight, child: buttonGroup),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
if (!narrow)
|
|
SizedBox(width: 132, child: Align(alignment: Alignment.centerRight, child: showButtons ? buttonGroup : statusText)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|