Compare commits

...

2 Commits

Author SHA1 Message Date
7baa3b48ca Merge remote-tracking branch 'origin/main' 2026-07-08 10:26:57 +08:00
8b3e5cce35 更新页面设计 2026-07-08 10:25:04 +08:00
3 changed files with 194 additions and 187 deletions

View File

@ -276,7 +276,7 @@ class _MessagePageState extends State<SCMessagePage> {
Widget _headMenu(RtmProvider provider) { Widget _headMenu(RtmProvider provider) {
return Container( return Container(
margin: EdgeInsetsDirectional.symmetric( margin: EdgeInsetsDirectional.symmetric(
horizontal: 25.w, horizontal: 20.w,
).copyWith(top: 10.w), ).copyWith(top: 10.w),
child: Column( child: Column(
spacing: 15.w, spacing: 15.w,
@ -351,9 +351,7 @@ class _MessagePageState extends State<SCMessagePage> {
}, },
), ),
SCDebounceWidget( SCDebounceWidget(
child: Stack( child: Row(
children: [
Row(
children: [ children: [
SizedBox(height: 3.w), SizedBox(height: 3.w),
Image.asset( Image.asset(
@ -361,8 +359,9 @@ class _MessagePageState extends State<SCMessagePage> {
width: 45.w, width: 45.w,
height: 45.w, height: 45.w,
), ),
SizedBox(width: 5.w), SizedBox(width: 6.w),
Column( Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
text( text(
@ -372,41 +371,21 @@ class _MessagePageState extends State<SCMessagePage> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
SizedBox(height: 3.w), SizedBox(height: 3.w),
text( Row(
children: [
Expanded(
child: text(
"[${SCAppLocalizations.of(context)!.newMessage}]", "[${SCAppLocalizations.of(context)!.newMessage}]",
fontSize: 12.sp, fontSize: 12.sp,
textColor: Colors.white54, textColor: Colors.white54,
), ),
),
SizedBox(width: 5.w),
_unreadBadge(provider.systemUnReadCount),
], ],
), ),
], ],
), ),
PositionedDirectional(
bottom: 0,
end: 10.w,
child: Visibility(
visible: provider.systemUnReadCount > 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.w),
alignment: Alignment.center,
height: 16.w,
decoration: BoxDecoration(
//gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]),
color: Colors.red,
shape: BoxShape.circle,
//borderRadius: BorderRadius.all(Radius.circular(7.w)),
),
child: Text(
"${provider.systemUnReadCount > 99 ? '99+' : provider.systemUnReadCount}",
style: TextStyle(
fontSize: 10.sp,
color: Color(0xffffffff),
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
),
),
),
),
), ),
], ],
), ),
@ -423,10 +402,6 @@ class _MessagePageState extends State<SCMessagePage> {
}, },
), ),
SCDebounceWidget( SCDebounceWidget(
child: Stack(
children: [
Container(
alignment: Alignment.center,
child: Row( child: Row(
children: [ children: [
SizedBox(height: 3.w), SizedBox(height: 3.w),
@ -435,8 +410,9 @@ class _MessagePageState extends State<SCMessagePage> {
width: 45.w, width: 45.w,
height: 45.w, height: 45.w,
), ),
SizedBox(width: 5.w), SizedBox(width: 6.w),
Column( Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
text( text(
@ -446,43 +422,22 @@ class _MessagePageState extends State<SCMessagePage> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
SizedBox(height: 3.w), SizedBox(height: 3.w),
text( Row(
children: [
Expanded(
child: text(
"[${SCAppLocalizations.of(context)!.newMessage}]", "[${SCAppLocalizations.of(context)!.newMessage}]",
fontSize: 12.sp, fontSize: 12.sp,
textColor: Colors.white54, textColor: Colors.white54,
), ),
),
SizedBox(width: 5.w),
_unreadBadge(provider.notifcationUnReadCount),
], ],
), ),
], ],
), ),
), ),
PositionedDirectional(
bottom: 0,
end: 10.w,
child: Visibility(
visible: provider.notifcationUnReadCount > 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.w),
alignment: Alignment.center,
height: 16.w,
decoration: BoxDecoration(
//gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]),
color: Colors.red,
shape: BoxShape.circle,
//borderRadius: BorderRadius.all(Radius.circular(7.w)),
),
child: Text(
"${provider.notifcationUnReadCount > 99 ? '99+' : provider.notifcationUnReadCount}",
style: TextStyle(
fontSize: 10.sp,
color: Color(0xffffffff),
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
),
),
),
),
),
], ],
), ),
onTap: () { onTap: () {
@ -498,4 +453,25 @@ class _MessagePageState extends State<SCMessagePage> {
), ),
); );
} }
Widget _unreadBadge(int unreadCount) {
return Visibility(
visible: unreadCount > 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.w),
alignment: Alignment.center,
height: 16.w,
decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: Text(
"${unreadCount > 99 ? '99+' : unreadCount}",
style: TextStyle(
fontSize: 10.sp,
color: Color(0xffffffff),
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
),
),
),
);
}
} }

View File

@ -569,7 +569,7 @@ class _ConversationItemState extends State<ConversationItem> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
head(url: _displayAvatar, width: 55.w), head(url: _displayAvatar, width: 45.w),
SizedBox(width: 6.w), SizedBox(width: 6.w),
Expanded( Expanded(
child: Column( child: Column(

View File

@ -157,30 +157,7 @@ class _MsgItemState extends State<MsgItem> {
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
horizontal: width(10), horizontal: width(10),
).copyWith(bottom: 6.w), ).copyWith(bottom: 6.w),
child: Column( child: _buildTextMessageItem(context),
children: [
_buildUserInfoLine(context),
SizedBox(height: 6.w),
Row(
children: [
Flexible(
// 使Flexible替代Expanded
child: GestureDetector(
child: _buildTextMessageBubble(),
onLongPress: () {
if (!(widget.msg.msg ?? "").startsWith(AtText.flag)) {
Clipboard.setData(
ClipboardData(text: widget.msg.msg ?? ''),
);
SCTts.show("Copied");
}
},
),
),
],
),
],
),
), ),
); );
} else { } else {
@ -212,10 +189,71 @@ class _MsgItemState extends State<MsgItem> {
} }
} }
Widget _buildTextMessageBubble() { Widget _buildTextMessageItem(BuildContext context) {
final userId = widget.msg.user?.id;
_clearStaleUserInfoIfNeeded(userId);
if (userId == null || userId.isEmpty) {
return _buildTextMessageItemWithUser(context, widget.msg.user);
}
final cachedUser = SCRoomUtils.roomUsersMap[userId];
if (cachedUser != null) {
return _buildTextMessageItemWithUser(context, cachedUser);
}
return FutureBuilder<SocialChatUserProfile?>(
future: _loadRoomUserInfoSilently(userId),
builder: (ct, AsyncSnapshot<SocialChatUserProfile?> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final user = snapshot.data!;
SCRoomUtils.roomUsersMap[user.id ?? ""] = user;
return _buildTextMessageItemWithUser(context, user);
}
return _buildTextMessageItemWithUser(context, widget.msg.user);
},
);
}
Widget _buildTextMessageItemWithUser(
BuildContext context,
SocialChatUserProfile? user,
) {
final badges = _activeWearBadges(user);
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildMessageUserAvatar(context, user),
SizedBox(width: 8.w),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUserNameAndLevels(user),
_buildLongMedals(userId: user?.id, fallbackBadges: badges),
_buildMedals(userId: user?.id, fallbackBadges: badges),
SizedBox(height: 2.w),
GestureDetector(
child: _buildTextMessageBubble(startMargin: 0),
onLongPress: () {
if (!(widget.msg.msg ?? "").startsWith(AtText.flag)) {
Clipboard.setData(
ClipboardData(text: widget.msg.msg ?? ''),
);
SCTts.show("Copied");
}
},
),
],
),
),
],
);
}
Widget _buildTextMessageBubble({double? startMargin}) {
final chatBubble = widget.msg.user?.getChatBox(); final chatBubble = widget.msg.user?.getChatBox();
final hasChatBubble = _resolveRoomChatBubbleImageUrl(chatBubble).isNotEmpty; final hasChatBubble = _resolveRoomChatBubbleImageUrl(chatBubble).isNotEmpty;
final bubbleStartMargin = 54.w; final bubbleStartMargin = startMargin ?? 54.w;
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final rowMaxWidth = final rowMaxWidth =
@ -1334,7 +1372,7 @@ class _MsgItemState extends State<MsgItem> {
}) { }) {
final hasUserId = (userId ?? "").trim().isNotEmpty; final hasUserId = (userId ?? "").trim().isNotEmpty;
if (!hasUserId && fallbackBadges.isEmpty) { if (!hasUserId && fallbackBadges.isEmpty) {
return SizedBox(height: 16.w); return const SizedBox.shrink();
} }
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
@ -1346,7 +1384,7 @@ class _MsgItemState extends State<MsgItem> {
longBadgeWidth: 51.w, longBadgeWidth: 51.w,
spacing: 5.w, spacing: 5.w,
maxBadges: 8, maxBadges: 8,
reserveSpace: true, reserveSpace: false,
), ),
); );
} }
@ -1837,14 +1875,7 @@ class _MsgItemState extends State<MsgItem> {
_buildUserInfoLine(BuildContext context) { _buildUserInfoLine(BuildContext context) {
final userId = widget.msg.user?.id; final userId = widget.msg.user?.id;
if ((widget.msg.needUpDataUserInfo) ?? false) { _clearStaleUserInfoIfNeeded(userId);
SCRoomUtils.roomUsersMap.remove(userId ?? "");
if ((userId ?? "").isNotEmpty) {
_userInfoFutures.remove(userId);
_userInfoFailedAt.remove(userId);
}
widget.msg.needUpDataUserInfo = false;
}
if (userId == null || userId.isEmpty) { if (userId == null || userId.isEmpty) {
return _buildPendingUserInfoLine(context); return _buildPendingUserInfoLine(context);
} }
@ -1865,6 +1896,17 @@ class _MsgItemState extends State<MsgItem> {
: _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!); : _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
} }
void _clearStaleUserInfoIfNeeded(String? userId) {
if ((widget.msg.needUpDataUserInfo) ?? false) {
SCRoomUtils.roomUsersMap.remove(userId ?? "");
if ((userId ?? "").isNotEmpty) {
_userInfoFutures.remove(userId);
_userInfoFailedAt.remove(userId);
}
widget.msg.needUpDataUserInfo = false;
}
}
Future<SocialChatUserProfile?> _loadRoomUserInfoSilently(String userId) { Future<SocialChatUserProfile?> _loadRoomUserInfoSilently(String userId) {
final failedAt = _userInfoFailedAt[userId]; final failedAt = _userInfoFailedAt[userId];
if (failedAt != null && if (failedAt != null &&
@ -1888,36 +1930,44 @@ class _MsgItemState extends State<MsgItem> {
}); });
} }
Widget _buildPendingUserInfoLine(BuildContext context) { Widget _buildMessageUserAvatar(
return Row( BuildContext context,
children: [ SocialChatUserProfile? user,
GestureDetector( ) {
return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
widget.onClick(widget.msg.user); widget.onClick(user);
}, },
child: Stack( child: Stack(
alignment: AlignmentDirectional.bottomEnd, alignment: AlignmentDirectional.bottomEnd,
children: [ children: [
head( head(
url: widget.msg.user?.userAvatar ?? "", url: user?.userAvatar ?? "",
width: 48.w, width: 48.w,
// headdress: msg.user?.getHeaddress()?.sourceUrl, // headdress: msg.user?.getHeaddress()?.sourceUrl,
), ),
Positioned( Positioned(
child: msgRoleTag(widget.msg.role ?? "", width: 14.w),
bottom: 2.w, bottom: 2.w,
right: 2.w, right: 2.w,
child: msgRoleTag(widget.msg.role ?? "", width: 14.w),
), ),
], ],
), ),
), );
}
Widget _buildPendingUserInfoLine(BuildContext context) {
return Row(
children: [
_buildMessageUserAvatar(context, widget.msg.user),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( Expanded(
child: SizedBox( child: ConstrainedBox(
height: 76.w, constraints: BoxConstraints(minHeight: 48.w),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildUserNameAndLevels(widget.msg.user), _buildUserNameAndLevels(widget.msg.user),
@ -1942,33 +1992,14 @@ class _MsgItemState extends State<MsgItem> {
_buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) { _buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) {
return Row( return Row(
children: [ children: [
GestureDetector( _buildMessageUserAvatar(context, user),
behavior: HitTestBehavior.opaque,
onTap: () {
widget.onClick(user);
},
child: Stack(
alignment: AlignmentDirectional.bottomEnd,
children: [
head(
url: user.userAvatar ?? "",
width: 48.w,
// headdress: msg.user?.getHeaddress()?.sourceUrl,
),
Positioned(
child: msgRoleTag(widget.msg.role ?? "", width: 14.w),
bottom: 2.w,
right: 2.w,
),
],
),
),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( Expanded(
child: SizedBox( child: ConstrainedBox(
height: 76.w, constraints: BoxConstraints(minHeight: 48.w),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildUserNameAndLevels(user), _buildUserNameAndLevels(user),