2228 lines
68 KiB
Dart
2228 lines
68 KiB
Dart
import 'dart:convert';
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:extended_text/extended_text.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:yumi/app_localizations.dart';
|
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
|
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
|
import 'package:yumi/app/constants/sc_screen.dart';
|
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
|
import 'package:yumi/shared/business_logic/models/res/mic_res.dart';
|
|
import 'package:yumi/shared/business_logic/usecases/sc_case.dart';
|
|
import 'package:yumi/modules/index/main_route.dart';
|
|
import 'package:yumi/ui_kit/widgets/sc_nine_patch_image.dart';
|
|
import 'package:yumi/ui_kit/widgets/badge/sc_user_badge_strip.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/room_emoji_asset_image.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/red_packet/room_red_packet_chat_message.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/red_packet/room_red_packet_models.dart';
|
|
import 'package:yumi/ui_kit/widgets/svga/sc_network_svga_widget.dart';
|
|
|
|
import '../../../shared/data_sources/models/enum/sc_activity_reward_props_type.dart';
|
|
import '../../../shared/data_sources/models/enum/sc_room_roles_type.dart';
|
|
import '../../components/sc_compontent.dart';
|
|
import '../../components/sc_tts.dart';
|
|
import '../../components/text/sc_text.dart';
|
|
|
|
const EdgeInsets _vipBubbleCenterSliceRatio = EdgeInsets.fromLTRB(
|
|
0.34,
|
|
0.34,
|
|
0.34,
|
|
0.34,
|
|
);
|
|
const EdgeInsets _vipBubbleSourceEdgeInset = EdgeInsets.all(2);
|
|
const String _cpSystemNoticeBubbleAsset =
|
|
'sc_images/room/cp_invite/sc_cp_system_notice_bubble.png';
|
|
const Rect _cpSystemNoticeBubbleCenterSlice = Rect.fromLTRB(42, 6, 209, 43);
|
|
|
|
///消息item
|
|
class MsgItem extends StatefulWidget {
|
|
final Function(SocialChatUserProfile? user) onClick;
|
|
final Msg msg;
|
|
|
|
const MsgItem({super.key, required this.msg, required this.onClick});
|
|
|
|
@override
|
|
State<MsgItem> createState() => _MsgItemState();
|
|
}
|
|
|
|
class _MsgItemState extends State<MsgItem> {
|
|
static const Duration _userInfoFailureRetryInterval = Duration(minutes: 5);
|
|
static final Map<String, Future<SocialChatUserProfile?>> _userInfoFutures =
|
|
{};
|
|
static final Map<String, DateTime> _userInfoFailedAt = {};
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
//gift
|
|
// if (msg.type == Roomtype.gift.code) {
|
|
// return _buildGiftMsg(context);
|
|
// }
|
|
|
|
///系统提示
|
|
if (widget.msg.type == SCRoomMsgType.systemTips ||
|
|
widget.msg.type == SCRoomMsgType.cpSystemNotice) {
|
|
return _systemTipsRoomItem(context);
|
|
}
|
|
|
|
///进入房间
|
|
if (widget.msg.type == SCRoomMsgType.joinRoom) {
|
|
return _enterRoomItem(context);
|
|
}
|
|
|
|
///礼物
|
|
if (widget.msg.type == SCRoomMsgType.gift ||
|
|
widget.msg.type == SCRoomMsgType.luckGiftAnimOther) {
|
|
return _buildGiftMsg(context);
|
|
}
|
|
|
|
///火箭用户中奖
|
|
if (SCRoomMsgType.isRocketRewardUserType(widget.msg.type)) {
|
|
return _buildRocketRewardMsg(context);
|
|
}
|
|
|
|
///火箭燃料加满倒计时
|
|
if (widget.msg.type == SCRoomMsgType.rocketLaunchNotice) {
|
|
return _buildRocketLaunchNoticeMsg(context);
|
|
}
|
|
|
|
///幸运礼物
|
|
if (widget.msg.type == SCRoomMsgType.gameLuckyGift) {
|
|
return _buildGameLuckyGiftMsg(context);
|
|
}
|
|
|
|
///幸运礼物
|
|
if (widget.msg.type == SCRoomMsgType.gameLuckyGift_5) {
|
|
return _buildGameLuckyGiftMsg_5(context);
|
|
}
|
|
|
|
///房间身份变动
|
|
if (widget.msg.type == SCRoomMsgType.roomRoleChange) {
|
|
return _buildRoleChangeMsg(context);
|
|
}
|
|
|
|
///踢出房间
|
|
if (widget.msg.type == SCRoomMsgType.qcfj) {
|
|
widget.msg.msg = SCAppLocalizations.of(context)!.kickRoomTips;
|
|
}
|
|
|
|
///图片消息
|
|
if (widget.msg.type == SCRoomMsgType.image) {
|
|
return _buildImageMsg(context);
|
|
}
|
|
|
|
///表情包聊天消息
|
|
if (widget.msg.type == SCRoomMsgType.emoticons) {
|
|
return _buildEmoticonMsg(context);
|
|
}
|
|
|
|
///掷骰子
|
|
if (widget.msg.type == SCRoomMsgType.roomDice) {
|
|
return _buildDiceMsg(context);
|
|
}
|
|
|
|
///石头剪刀布
|
|
if (widget.msg.type == SCRoomMsgType.roomRPS) {
|
|
return _buildRPSMsg(context);
|
|
}
|
|
|
|
///幸运数字
|
|
if (widget.msg.type == SCRoomMsgType.roomLuckNumber) {
|
|
return _buildLuckNumberMsg(context);
|
|
}
|
|
|
|
///房间红包
|
|
if (widget.msg.type == SCRoomMsgType.roomRedPacket) {
|
|
return _buildRoomRedPacketMsg(context);
|
|
}
|
|
if (widget.msg.type == SCRoomMsgType.roomRedPacketClaim) {
|
|
return _buildRoomRedPacketClaimMsg(context);
|
|
}
|
|
if (widget.msg.user != null) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
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 {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(8.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.all(Radius.circular(15.w)),
|
|
),
|
|
child: Text(
|
|
widget.msg.msg ?? '',
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
textAlign: TextAlign.left,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget _buildTextMessageBubble() {
|
|
final chatBubble = widget.msg.user?.getChatBox();
|
|
final hasChatBubble = _resolveRoomChatBubbleImageUrl(chatBubble).isNotEmpty;
|
|
final bubbleStartMargin = 54.w;
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final rowMaxWidth =
|
|
constraints.maxWidth.isFinite
|
|
? constraints.maxWidth
|
|
: ScreenUtil().screenWidth;
|
|
final availableBubbleWidth = math.max(
|
|
1.0,
|
|
rowMaxWidth - bubbleStartMargin,
|
|
);
|
|
if (hasChatBubble) {
|
|
return _buildVipTextMessageBubble(
|
|
chatBubble,
|
|
maxBubbleWidth: availableBubbleWidth,
|
|
startMargin: bubbleStartMargin,
|
|
);
|
|
}
|
|
|
|
return Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: math.min(
|
|
ScreenUtil().screenWidth * 0.7,
|
|
availableBubbleWidth,
|
|
),
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: bubbleStartMargin,
|
|
).copyWith(bottom: 8.w),
|
|
child: ExtendedText(
|
|
widget.msg.msg ?? '',
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
softWrap: true,
|
|
specialTextSpanBuilder: AtTextSpanBuilder(onTapCall: (userId) {}),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildRoomRedPacketMsg(BuildContext context) {
|
|
final data = RoomRedPacketUiData.fromMessagePayload(
|
|
payload: widget.msg.msg,
|
|
fallbackUser: widget.msg.user,
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (widget.msg.user != null) ...[
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
],
|
|
RoomRedPacketChatMessage(data: data),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRoomRedPacketClaimMsg(BuildContext context) {
|
|
final localizations = SCAppLocalizations.of(context)!;
|
|
final packetSenderName =
|
|
(widget.msg.msg ?? '').trim().isNotEmpty
|
|
? (widget.msg.msg ?? '').trim()
|
|
: localizations.redEnvelope;
|
|
final message = localizations.roomRedPacketReceivedMessage(
|
|
_formatAwardAmount(widget.msg.number),
|
|
packetSenderName,
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (widget.msg.user != null) ...[
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
],
|
|
Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.7,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child: Text(
|
|
message,
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
textAlign: TextAlign.start,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildVipTextMessageBubble(
|
|
PropsResources? resource, {
|
|
required double maxBubbleWidth,
|
|
required double startMargin,
|
|
}) {
|
|
final messageText = widget.msg.msg ?? "";
|
|
final textStyle = TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
);
|
|
final imageUrl = _resolveRoomChatBubbleImageUrl(resource);
|
|
final multilineMaxBubbleWidth = math.min(maxBubbleWidth, 300.w);
|
|
final multilineMaxTextWidth = math.max(
|
|
82.w,
|
|
multilineMaxBubbleWidth - 64.w,
|
|
);
|
|
final multilineTextPainter = TextPainter(
|
|
text: TextSpan(text: messageText, style: textStyle),
|
|
textDirection: Directionality.of(context),
|
|
)..layout(maxWidth: multilineMaxTextWidth);
|
|
if (multilineTextPainter.computeLineMetrics().length > 1) {
|
|
return _buildMultilineVipTextMessageBubble(
|
|
messageText: messageText,
|
|
textStyle: textStyle,
|
|
imageUrl: imageUrl,
|
|
textSize: multilineTextPainter.size,
|
|
maxBubbleWidth: multilineMaxBubbleWidth,
|
|
startMargin: startMargin,
|
|
);
|
|
}
|
|
|
|
return _buildSingleLineVipTextMessageBubble(
|
|
messageText: messageText,
|
|
textStyle: textStyle,
|
|
imageUrl: imageUrl,
|
|
maxBubbleWidth: maxBubbleWidth,
|
|
startMargin: startMargin,
|
|
);
|
|
}
|
|
|
|
Widget _buildMultilineVipTextMessageBubble({
|
|
required String messageText,
|
|
required TextStyle textStyle,
|
|
required String imageUrl,
|
|
required Size textSize,
|
|
required double maxBubbleWidth,
|
|
required double startMargin,
|
|
}) {
|
|
final leftPadding = 22.w;
|
|
final rightPadding = 42.w;
|
|
final topPadding = 16.w;
|
|
final bottomPadding = 24.w;
|
|
final bubbleWidth = math
|
|
.max(126.w, textSize.width + leftPadding + rightPadding)
|
|
.clamp(126.w, maxBubbleWidth);
|
|
final textMaxWidth = math.max(
|
|
64.w,
|
|
bubbleWidth - leftPadding - rightPadding,
|
|
);
|
|
final textPainter = TextPainter(
|
|
text: TextSpan(text: messageText, style: textStyle),
|
|
textDirection: Directionality.of(context),
|
|
)..layout(maxWidth: textMaxWidth);
|
|
final textBoxHeight = textPainter.size.height + 2.w;
|
|
final bubbleHeight = math.max(
|
|
76.w,
|
|
textBoxHeight + topPadding + bottomPadding,
|
|
);
|
|
|
|
return Container(
|
|
width: bubbleWidth,
|
|
height: bubbleHeight,
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: startMargin,
|
|
).copyWith(bottom: 8.w),
|
|
child: Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
_buildVipBubbleBackground(
|
|
url: imageUrl,
|
|
width: bubbleWidth,
|
|
height: bubbleHeight,
|
|
fallback: SizedBox(width: bubbleWidth, height: bubbleHeight),
|
|
),
|
|
PositionedDirectional(
|
|
start: leftPadding,
|
|
top: topPadding,
|
|
width: textMaxWidth,
|
|
child: ExtendedText(
|
|
messageText,
|
|
style: textStyle,
|
|
softWrap: true,
|
|
specialTextSpanBuilder: AtTextSpanBuilder(onTapCall: (userId) {}),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSingleLineVipTextMessageBubble({
|
|
required String messageText,
|
|
required TextStyle textStyle,
|
|
required String imageUrl,
|
|
required double maxBubbleWidth,
|
|
required double startMargin,
|
|
}) {
|
|
final leftPadding = 22.w;
|
|
final rightPadding = 24.w;
|
|
final topPadding = 12.w;
|
|
final bottomPadding = 16.w;
|
|
final minSkinWidth = 96.w;
|
|
final minSkinHeight = 58.w;
|
|
final textWidthSafety = 10.w;
|
|
final maxTextWidth = math.max(
|
|
1.0,
|
|
maxBubbleWidth - leftPadding - rightPadding,
|
|
);
|
|
final textSize = _measureMessageTextSize(
|
|
text: messageText,
|
|
style: textStyle,
|
|
maxWidth: maxTextWidth,
|
|
);
|
|
final bubbleWidth = math.min(
|
|
maxBubbleWidth,
|
|
math.max(
|
|
minSkinWidth,
|
|
textSize.width + textWidthSafety + leftPadding + rightPadding,
|
|
),
|
|
);
|
|
final textMaxWidth = math.max(
|
|
1.0,
|
|
bubbleWidth - leftPadding - rightPadding,
|
|
);
|
|
final bubbleHeight = math.max(
|
|
minSkinHeight,
|
|
textSize.height + topPadding + bottomPadding,
|
|
);
|
|
|
|
return Container(
|
|
width: bubbleWidth,
|
|
height: bubbleHeight,
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: startMargin,
|
|
).copyWith(bottom: 8.w),
|
|
child: Stack(
|
|
clipBehavior: Clip.none,
|
|
fit: StackFit.expand,
|
|
children: [
|
|
Positioned.fill(
|
|
child: _buildVipBubbleSkin(
|
|
url: imageUrl,
|
|
width: math.max(bubbleWidth, minSkinWidth),
|
|
height: math.max(bubbleHeight, minSkinHeight),
|
|
alignment: AlignmentDirectional.centerStart,
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsetsDirectional.fromSTEB(
|
|
leftPadding,
|
|
topPadding,
|
|
rightPadding,
|
|
bottomPadding,
|
|
),
|
|
child: Align(
|
|
alignment: AlignmentDirectional.centerStart,
|
|
child: SizedBox(
|
|
width: textMaxWidth,
|
|
child: ExtendedText(
|
|
messageText,
|
|
style: textStyle,
|
|
maxLines: 1,
|
|
softWrap: false,
|
|
specialTextSpanBuilder: AtTextSpanBuilder(
|
|
onTapCall: (userId) {},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildVipBubbleSkin({
|
|
required String url,
|
|
required double width,
|
|
required double height,
|
|
required AlignmentGeometry alignment,
|
|
}) {
|
|
return OverflowBox(
|
|
alignment: alignment,
|
|
minWidth: width,
|
|
maxWidth: width,
|
|
minHeight: height,
|
|
maxHeight: height,
|
|
child:
|
|
SCNetworkSvgaWidget.isSvga(url)
|
|
? SCNetworkSvgaWidget(
|
|
resource: url,
|
|
width: width,
|
|
height: height,
|
|
fit: BoxFit.fill,
|
|
loop: true,
|
|
centerSliceRatio: _vipBubbleCenterSliceRatio,
|
|
sourceEdgeInset: _vipBubbleSourceEdgeInset,
|
|
fallback: SizedBox(width: width, height: height),
|
|
)
|
|
: _buildStaticVipBubbleImage(
|
|
url: url,
|
|
width: width,
|
|
height: height,
|
|
fallback: SizedBox(width: width, height: height),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildVipBubbleBackground({
|
|
required String url,
|
|
required double width,
|
|
required double height,
|
|
required Widget fallback,
|
|
}) {
|
|
if (SCNetworkSvgaWidget.isSvga(url)) {
|
|
return SCNetworkSvgaWidget(
|
|
resource: url,
|
|
width: width,
|
|
height: height,
|
|
fit: BoxFit.fill,
|
|
loop: true,
|
|
centerSliceRatio: _vipBubbleCenterSliceRatio,
|
|
sourceEdgeInset: _vipBubbleSourceEdgeInset,
|
|
fallback: fallback,
|
|
);
|
|
}
|
|
return _buildStaticVipBubbleImage(
|
|
url: url,
|
|
width: width,
|
|
height: height,
|
|
fallback: fallback,
|
|
);
|
|
}
|
|
|
|
Widget _buildStaticVipBubbleImage({
|
|
required String url,
|
|
required double width,
|
|
required double height,
|
|
Widget? fallback,
|
|
}) {
|
|
final imageUrl = url.trim();
|
|
if (imageUrl.isEmpty) {
|
|
return fallback ?? SizedBox(width: width, height: height);
|
|
}
|
|
|
|
return SCNinePatchImage(
|
|
url: imageUrl,
|
|
width: width,
|
|
height: height,
|
|
centerSliceRatio: _vipBubbleCenterSliceRatio,
|
|
sourceEdgeInset: _vipBubbleSourceEdgeInset,
|
|
fallback: fallback ?? SizedBox(width: width, height: height),
|
|
);
|
|
}
|
|
|
|
Size _measureMessageTextSize({
|
|
required String text,
|
|
required TextStyle style,
|
|
required double maxWidth,
|
|
}) {
|
|
final textPainter = TextPainter(
|
|
text: TextSpan(text: text, style: style),
|
|
textDirection: Directionality.of(context),
|
|
)..layout(maxWidth: maxWidth);
|
|
return textPainter.size;
|
|
}
|
|
|
|
String _resolveRoomChatBubbleImageUrl(PropsResources? resource) {
|
|
final candidates = <String?>[
|
|
resource?.roomSendCoverUrl,
|
|
resource?.roomOpenedUrl,
|
|
resource?.roomNotOpenedUrl,
|
|
resource?.imSendCoverUrl,
|
|
resource?.imOpenedUrl,
|
|
resource?.imOpenedUrlTwo,
|
|
resource?.imNotOpenedUrl,
|
|
resource?.cover,
|
|
resource?.expand,
|
|
resource?.sourceUrl,
|
|
];
|
|
return _firstNonBlank([
|
|
...candidates.where((value) => SCNetworkSvgaWidget.isSvga(value)),
|
|
...candidates,
|
|
]);
|
|
}
|
|
|
|
String _firstNonBlank(Iterable<String?> values) {
|
|
for (final value in values) {
|
|
final text = value?.trim();
|
|
if (text != null && text.isNotEmpty) {
|
|
return text;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
///系统提示
|
|
_systemTipsRoomItem(BuildContext context) {
|
|
if (widget.msg.type == SCRoomMsgType.cpSystemNotice) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
width: 315.w,
|
|
constraints: BoxConstraints(minHeight: 49.w),
|
|
padding: EdgeInsetsDirectional.fromSTEB(28.w, 6.w, 18.w, 6.w),
|
|
margin: EdgeInsets.symmetric(horizontal: 10.w).copyWith(bottom: 8.w),
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage(_cpSystemNoticeBubbleAsset),
|
|
fit: BoxFit.fill,
|
|
centerSlice: _cpSystemNoticeBubbleCenterSlice,
|
|
),
|
|
),
|
|
child: Align(
|
|
alignment: Alignment.center,
|
|
child: Text(
|
|
widget.msg.msg ?? '',
|
|
maxLines: 4,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
height: 1.35,
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
decoration: TextDecoration.none,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(8.w),
|
|
vertical: width(8),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.all(Radius.circular(15.w)),
|
|
),
|
|
child: Text(
|
|
widget.msg.msg ?? '',
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
height: 1.5.w,
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
textAlign: TextAlign.left,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
///火箭用户中奖
|
|
_buildRocketRewardMsg(BuildContext context) {
|
|
List<InlineSpan> spans = [];
|
|
final nickname = Msg._firstNonBlankString([
|
|
widget.msg.user?.userNickname,
|
|
widget.msg.user?.account,
|
|
widget.msg.user?.id,
|
|
SCAppLocalizations.of(context)?.member,
|
|
'Member',
|
|
]);
|
|
|
|
spans.add(
|
|
TextSpan(
|
|
text: "$nickname ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: SocialChatTheme.primaryColor,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.inRocket,
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
WidgetSpan(alignment: PlaceholderAlignment.middle, child: _rocketIcon()),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.obtain,
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child:
|
|
widget.msg.role == SCActivityRewardATPropsType.GOLD.name
|
|
? Image.asset("sc_images/general/sc_icon_jb.png", width: 18.w)
|
|
: netImage(url: widget.msg.msg ?? "", width: 18.w),
|
|
),
|
|
);
|
|
Widget text = Text.rich(
|
|
TextSpan(children: spans),
|
|
textAlign: TextAlign.left,
|
|
strutStyle: StrutStyle(
|
|
height: 1.8, // 行高倍数
|
|
fontWeight: FontWeight.w500,
|
|
forceStrutHeight: true, // 强制应用行高
|
|
),
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
|
margin: EdgeInsets.only(left: 34.w).copyWith(bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.all(Radius.circular(15.w)),
|
|
),
|
|
child: text,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _rocketIcon() {
|
|
final iconUrl = widget.msg.rocketIconUrl?.trim() ?? '';
|
|
if (iconUrl.isEmpty) {
|
|
return SizedBox(width: 4.w);
|
|
}
|
|
return netImage(url: iconUrl, width: 18.w);
|
|
}
|
|
|
|
Widget _buildRocketLaunchNoticeMsg(BuildContext context) {
|
|
final seconds =
|
|
(widget.msg.number ?? num.tryParse(widget.msg.msg ?? '') ?? 10).toInt();
|
|
final template =
|
|
SCAppLocalizations.of(
|
|
context,
|
|
)?.roomRocketFuelFullNotice(seconds.toString()) ??
|
|
'Rocket fuel is full! Launching in ${seconds}s!';
|
|
final iconUrl = widget.msg.rocketIconUrl?.trim() ?? '';
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
constraints: BoxConstraints(minHeight: 42.w, maxWidth: 304.w),
|
|
padding: EdgeInsetsDirectional.only(
|
|
start: 12.w,
|
|
end: 10.w,
|
|
top: 8.w,
|
|
bottom: 8.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(start: 10.w, end: 36.w, bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF1F2FF).withValues(alpha: 0.92),
|
|
borderRadius: BorderRadius.circular(9.w),
|
|
border: Border.all(color: const Color(0xFF6672FF), width: 1.w),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
template,
|
|
textScaler: TextScaler.noScaling,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
color: const Color(0xFF6672FF),
|
|
fontSize: sp(13),
|
|
height: 1.2,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
SizedBox(width: 8.w),
|
|
SizedBox(
|
|
width: 42.w,
|
|
height: 28.w,
|
|
child:
|
|
iconUrl.isEmpty
|
|
? Icon(
|
|
Icons.rocket_launch_rounded,
|
|
color: const Color(0xFFB8BEFA),
|
|
size: 28.w,
|
|
)
|
|
: netImage(
|
|
url: iconUrl,
|
|
width: 42.w,
|
|
height: 28.w,
|
|
fit: BoxFit.contain,
|
|
noDefaultImg: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
///幸运礼物
|
|
_buildGameLuckyGiftMsg(BuildContext context) {
|
|
List<InlineSpan> spans = [];
|
|
|
|
spans.add(
|
|
TextSpan(
|
|
text: "${widget.msg.user?.userNickname} ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: SocialChatTheme.primaryColor,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer:
|
|
TapGestureRecognizer()
|
|
..onTap = () {
|
|
widget.onClick(widget.msg.user);
|
|
},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.sendTo,
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: " ${widget.msg.toUser?.userNickname} ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: SocialChatTheme.primaryColor,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer:
|
|
TapGestureRecognizer()
|
|
..onTap = () {
|
|
widget.onClick(widget.msg.toUser);
|
|
},
|
|
),
|
|
);
|
|
SCGlobalConfig.lang == "ar"
|
|
? spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: Image.asset("sc_images/general/sc_icon_jb.png", width: 18.w),
|
|
),
|
|
)
|
|
: spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: netImage(url: widget.msg.gift?.giftPhoto ?? "", width: 18.w),
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: " ${SCAppLocalizations.of(context)!.obtain}",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: SocialChatTheme.primaryColor,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: " ${_formatAwardAmount(widget.msg.awardAmount)}",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: SocialChatTheme.primaryColor,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
SCGlobalConfig.lang == "ar"
|
|
? spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: netImage(url: widget.msg.gift?.giftPhoto ?? "", width: 18.w),
|
|
),
|
|
)
|
|
: spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: Image.asset("sc_images/general/sc_icon_jb.png", width: 18.w),
|
|
),
|
|
);
|
|
Widget text = Text.rich(
|
|
TextSpan(children: spans),
|
|
textAlign: TextAlign.left,
|
|
strutStyle: StrutStyle(
|
|
height: 1.3, // 行高倍数
|
|
fontWeight: FontWeight.w500,
|
|
forceStrutHeight: true, // 强制应用行高
|
|
),
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
|
margin: EdgeInsets.only(left: 34.w).copyWith(bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.all(Radius.circular(15.w)),
|
|
),
|
|
child: text,
|
|
),
|
|
);
|
|
}
|
|
|
|
///幸运礼物中奖5倍以及以上
|
|
_buildGameLuckyGiftMsg_5(BuildContext context) {
|
|
List<InlineSpan> spans = [];
|
|
spans.add(
|
|
TextSpan(
|
|
text: "${widget.msg.user?.userNickname} ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Color(0xffFEF129),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer:
|
|
TapGestureRecognizer()
|
|
..onTap = () {
|
|
widget.onClick(widget.msg.user);
|
|
},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.get,
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: " ${_formatAwardAmount(widget.msg.awardAmount)} ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Color(0xffFEF129),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: Image.asset(
|
|
"sc_images/general/sc_icon_jb.png",
|
|
width: 18.w,
|
|
height: 18.w,
|
|
),
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: " from ",
|
|
style: TextStyle(
|
|
fontSize: sp(12),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: netImage(
|
|
url: widget.msg.gift?.giftPhoto ?? "",
|
|
width: 18.w,
|
|
height: 18.w,
|
|
),
|
|
),
|
|
);
|
|
Widget text = Text.rich(
|
|
TextSpan(children: spans),
|
|
textAlign: TextAlign.left,
|
|
strutStyle: StrutStyle(
|
|
height: 1.2, // 行高倍数
|
|
fontWeight: FontWeight.w500,
|
|
forceStrutHeight: true, // 强制应用行高
|
|
),
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
constraints: BoxConstraints(minHeight: 68.w),
|
|
padding: EdgeInsets.symmetric(vertical: 5.w),
|
|
margin: EdgeInsets.only(left: 34.w).copyWith(bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage("sc_images/room/sc_icon_luck_gift_msg_n_bg.png"),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
SizedBox(width: 10.w),
|
|
Expanded(child: text),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage(
|
|
"sc_images/room/sc_icon_luck_gift_msg_n_ball.png",
|
|
),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
alignment: AlignmentDirectional.center,
|
|
width: 55.w,
|
|
height: 60.w,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SizedBox(height: 10.w),
|
|
buildNumForGame(widget.msg.msg ?? "0", size: 18.w),
|
|
SizedBox(height: 3.w),
|
|
SCGlobalConfig.lang == "ar"
|
|
? Image.asset(
|
|
"sc_images/room/sc_icon_times_text_ar.png",
|
|
height: 12.w,
|
|
)
|
|
: Image.asset(
|
|
"sc_images/room/sc_icon_times_text_en.png",
|
|
height: 10.w,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(width: 15.w),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _formatAwardAmount(num? awardAmount) {
|
|
if (awardAmount == null) {
|
|
return "0";
|
|
}
|
|
if (awardAmount % 1 == 0) {
|
|
return awardAmount.toInt().toString();
|
|
}
|
|
return awardAmount.toString();
|
|
}
|
|
|
|
Widget _buildGiftMsg(BuildContext context) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
padding: EdgeInsets.symmetric(horizontal: width(5.w), vertical: width(4)),
|
|
margin: EdgeInsets.symmetric(horizontal: width(10)).copyWith(bottom: 8.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
// 使用Flexible替代Expanded
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 4.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child: Text.rich(
|
|
TextSpan(
|
|
children: [
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.sendTo,
|
|
style: TextStyle(
|
|
fontSize: 13.sp,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
TextSpan(text: " "),
|
|
TextSpan(
|
|
text: _giftTargetName(context),
|
|
style: TextStyle(
|
|
fontSize: 13.sp,
|
|
color: SocialChatTheme.primaryColor,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
TextSpan(text: " "),
|
|
WidgetSpan(
|
|
child: netImage(
|
|
url: widget.msg.gift?.giftPhoto ?? "",
|
|
width: 22.w,
|
|
height: 22.w,
|
|
),
|
|
),
|
|
TextSpan(text: " "),
|
|
TextSpan(
|
|
text: "x${widget.msg.number}",
|
|
style: TextStyle(
|
|
fontSize: 13.sp,
|
|
color: Colors.white,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
],
|
|
),
|
|
textAlign: TextAlign.left,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
String _giftTargetName(BuildContext context) {
|
|
final targetUserIds =
|
|
widget.msg.targetUserIds
|
|
?.map((id) => id.trim())
|
|
.where((id) => id.isNotEmpty)
|
|
.toSet();
|
|
if (targetUserIds != null && targetUserIds.length > 1) {
|
|
return SCAppLocalizations.of(context)!.multiple;
|
|
}
|
|
return widget.msg.toUser?.userNickname ?? "";
|
|
}
|
|
|
|
///进入房间
|
|
_enterRoomItem(BuildContext context) {
|
|
List<InlineSpan> spans = [];
|
|
if (widget.msg.role != null && widget.msg.role != "0") {
|
|
spans.add(
|
|
WidgetSpan(
|
|
alignment: PlaceholderAlignment.middle,
|
|
child: Padding(
|
|
padding: EdgeInsets.only(right: 3.w),
|
|
child: msgRoleTag(widget.msg.role ?? "", width: 18.w),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
spans.add(
|
|
TextSpan(
|
|
text: "${widget.msg.user?.userNickname ?? ''} ",
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: _roleNameColor(widget.msg.role ?? ""),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
spans.add(
|
|
TextSpan(
|
|
text: SCAppLocalizations.of(context)!.joinRoomTips,
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
);
|
|
Widget text = Text.rich(
|
|
TextSpan(children: spans),
|
|
textAlign: TextAlign.left,
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(8.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 8.w),
|
|
child: text,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildUserNameAndLevels(SocialChatUserProfile? user) {
|
|
return SizedBox(
|
|
height: 23.w,
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Flexible(
|
|
fit: FlexFit.loose,
|
|
child: socialchatNickNameText(
|
|
user?.userNickname ?? "",
|
|
maxWidth: 112.w,
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 13.sp,
|
|
type: user?.getVIP()?.name ?? "",
|
|
needScroll: (user?.userNickname?.characters.length ?? 0) > 10,
|
|
),
|
|
),
|
|
SizedBox(width: 4.w),
|
|
getWealthLevel(
|
|
user?.wealthLevel ?? 0,
|
|
width: 42.w,
|
|
height: 20.w,
|
|
fontSize: 9.sp,
|
|
),
|
|
SizedBox(width: 2.w),
|
|
getUserLevel(
|
|
user?.charmLevel ?? 0,
|
|
width: 42.w,
|
|
height: 20.w,
|
|
fontSize: 9.sp,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
List<WearBadge> _activeWearBadges(SocialChatUserProfile? user) {
|
|
return user?.wearBadge?.where((item) => item.use ?? false).toList() ??
|
|
const <WearBadge>[];
|
|
}
|
|
|
|
Widget _buildMedals({
|
|
required String? userId,
|
|
List<WearBadge> fallbackBadges = const <WearBadge>[],
|
|
}) {
|
|
final hasUserId = (userId ?? "").trim().isNotEmpty;
|
|
if (!hasUserId && fallbackBadges.isEmpty) {
|
|
return SizedBox(height: 16.w);
|
|
}
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: SCUserBadgeStrip(
|
|
userId: userId,
|
|
fallbackBadges: fallbackBadges,
|
|
height: 28.w,
|
|
badgeHeight: 25.w,
|
|
longBadgeWidth: 51.w,
|
|
spacing: 5.w,
|
|
maxBadges: 8,
|
|
reserveSpace: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLongMedals({
|
|
required String? userId,
|
|
List<WearBadge> fallbackBadges = const <WearBadge>[],
|
|
}) {
|
|
final hasUserId = (userId ?? "").trim().isNotEmpty;
|
|
if (!hasUserId && fallbackBadges.isEmpty) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: SCUserBadgeStrip(
|
|
userId: userId,
|
|
fallbackBadges: fallbackBadges,
|
|
displayScope: SCUserBadgeDisplayScope.long,
|
|
height: 18.w,
|
|
badgeHeight: 16.w,
|
|
longBadgeWidth: 50.w,
|
|
spacing: 3.w,
|
|
maxBadges: 8,
|
|
reserveSpace: false,
|
|
),
|
|
);
|
|
}
|
|
|
|
int _msgColor(String type) {
|
|
switch (type) {
|
|
case SCRoomMsgType.joinRoom:
|
|
case SCRoomMsgType.systemTips:
|
|
case SCRoomMsgType.welcome:
|
|
return 0xFFFFFFFF;
|
|
case SCRoomMsgType.cpSystemNotice:
|
|
return 0xFF6F536D;
|
|
case SCRoomMsgType.gift:
|
|
return 0xFFFFFFFF;
|
|
case SCRoomMsgType.shangMai:
|
|
case SCRoomMsgType.xiaMai:
|
|
case SCRoomMsgType.qcfj:
|
|
case SCRoomMsgType.killXiaMai:
|
|
return 0xFFFFFFFF;
|
|
// case type.text:
|
|
// return _mapTextColor();
|
|
// break;
|
|
default:
|
|
return 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
_roleNameColor(String role) {
|
|
if (SCRoomRolesType.HOMEOWNER.name == role) {
|
|
return Color(0xff2AC0F2);
|
|
} else if (SCRoomRolesType.ADMIN.name == role) {
|
|
return Color(0xffF9A024);
|
|
} else if (SCRoomRolesType.MEMBER.name == role) {
|
|
return Color(0xff28ED77);
|
|
} else {
|
|
return Colors.white;
|
|
}
|
|
}
|
|
|
|
Widget _buildRoleChangeMsg(BuildContext context) {
|
|
var text = "";
|
|
if (SCRoomRolesType.ADMIN.name == widget.msg.msg) {
|
|
text = SCAppLocalizations.of(context)!.adminByHomeowner;
|
|
} else if (SCRoomRolesType.MEMBER.name == widget.msg.msg) {
|
|
text = SCAppLocalizations.of(context)!.memberByHomeowner;
|
|
} else {
|
|
text = SCAppLocalizations.of(context)!.touristByHomeowner;
|
|
}
|
|
List<InlineSpan> spans = [];
|
|
if (widget.msg.toUser != null) {
|
|
spans.add(
|
|
TextSpan(
|
|
text: "${widget.msg.toUser?.userNickname ?? ''} ",
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Color(0xff2AC0F2),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
recognizer: TapGestureRecognizer()..onTap = () {},
|
|
),
|
|
);
|
|
}
|
|
spans.add(
|
|
TextSpan(
|
|
text: text,
|
|
style: TextStyle(
|
|
fontSize: sp(13),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
);
|
|
Widget textView = Text.rich(
|
|
TextSpan(children: spans),
|
|
textAlign: TextAlign.left,
|
|
);
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(8.w),
|
|
vertical: width(8),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 8.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.all(Radius.circular(15.w)),
|
|
),
|
|
child: textView,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildImageMsg(BuildContext context) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
// 使用Flexible替代Expanded
|
|
child: GestureDetector(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 4.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child: netImage(url: widget.msg.msg ?? ""),
|
|
),
|
|
onTap: () {
|
|
String encodedUrls = Uri.encodeComponent(
|
|
jsonEncode([widget.msg.msg]),
|
|
);
|
|
SCNavigatorUtils.push(
|
|
context,
|
|
"${SCMainRoute.imagePreview}?imageUrls=$encodedUrls&initialIndex=0",
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildEmoticonMsg(BuildContext context) {
|
|
final emojiPath = widget.msg.msg ?? "";
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.45,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 8.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child: _buildEmojiImage(
|
|
emojiPath,
|
|
width: 72.w,
|
|
height: 72.w,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildEmojiImage(
|
|
String path, {
|
|
required double width,
|
|
required double height,
|
|
}) {
|
|
if (path.isEmpty) {
|
|
return SizedBox(width: width, height: height);
|
|
}
|
|
return RoomEmojiAssetImage(
|
|
key: ValueKey(path),
|
|
asset: path,
|
|
width: width,
|
|
height: height,
|
|
);
|
|
}
|
|
|
|
Widget _buildDiceMsg(BuildContext context) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
// 使用Flexible替代Expanded
|
|
child: GestureDetector(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 4.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child:
|
|
(widget.msg.hasPlayedAnimation ?? false)
|
|
? Image.asset(
|
|
"sc_images/room/sc_icon_dice_${widget.msg.msg}.png",
|
|
height: 35.w,
|
|
)
|
|
: FutureBuilder<void>(
|
|
future: Future.delayed(
|
|
Duration(milliseconds: 2000),
|
|
),
|
|
// 传入Future
|
|
builder: (
|
|
BuildContext context,
|
|
AsyncSnapshot<void> snapshot,
|
|
) {
|
|
// 根据snapshot的状态来构建UI
|
|
if (snapshot.connectionState ==
|
|
ConnectionState.done) {
|
|
widget.msg.hasPlayedAnimation = true;
|
|
return Image.asset(
|
|
"sc_images/room/sc_icon_dice_${widget.msg.msg}.png",
|
|
height: 35.w,
|
|
);
|
|
} else {
|
|
return Image.asset(
|
|
"sc_images/room/sc_icon_dice_animl.webp",
|
|
height: 35.w,
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
onTap: () {},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRPSMsg(BuildContext context) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
// 使用Flexible替代Expanded
|
|
child: GestureDetector(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 4.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child:
|
|
(widget.msg.hasPlayedAnimation ?? false)
|
|
? Image.asset(
|
|
"sc_images/room/sc_icon_rps_${widget.msg.msg}.png",
|
|
height: 35.w,
|
|
)
|
|
: FutureBuilder<void>(
|
|
future: Future.delayed(
|
|
Duration(milliseconds: 2000),
|
|
),
|
|
// 传入Future
|
|
builder: (
|
|
BuildContext context,
|
|
AsyncSnapshot<void> snapshot,
|
|
) {
|
|
// 根据snapshot的状态来构建UI
|
|
if (snapshot.connectionState ==
|
|
ConnectionState.done) {
|
|
widget.msg.hasPlayedAnimation = true;
|
|
return Image.asset(
|
|
"sc_images/room/sc_icon_rps_${widget.msg.msg}.png",
|
|
height: 35.w,
|
|
);
|
|
} else {
|
|
return Image.asset(
|
|
"sc_images/room/sc_icon_rps_animal.webp",
|
|
height: 35.w,
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
onTap: () {},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLuckNumberMsg(BuildContext context) {
|
|
return Container(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: width(5.w),
|
|
vertical: width(4),
|
|
),
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: width(10),
|
|
).copyWith(bottom: 6.w),
|
|
child: Column(
|
|
children: [
|
|
_buildUserInfoLine(context),
|
|
SizedBox(height: 6.w),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
// 使用Flexible替代Expanded
|
|
child: GestureDetector(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black38,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(15.w),
|
|
bottomLeft: Radius.circular(15.w),
|
|
bottomRight: Radius.circular(15.w),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10.w,
|
|
vertical: 4.w,
|
|
),
|
|
margin: EdgeInsetsDirectional.only(
|
|
start: 54.w,
|
|
).copyWith(bottom: 8.w),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SCGlobalConfig.lang == "ar"
|
|
? Image.asset(
|
|
"sc_images/room/sc_icon_luck_num_text_ar.png",
|
|
height: 20.w,
|
|
)
|
|
: Image.asset(
|
|
"sc_images/room/sc_icon_luck_num_text_en.png",
|
|
height: 20.w,
|
|
),
|
|
buildNumForRoomLuckNum(
|
|
widget.msg.msg ?? "",
|
|
size: 20.w,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
onTap: () {},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
_buildUserInfoLine(BuildContext context) {
|
|
final userId = widget.msg.user?.id;
|
|
if ((widget.msg.needUpDataUserInfo) ?? false) {
|
|
SCRoomUtils.roomUsersMap.remove(userId ?? "");
|
|
if ((userId ?? "").isNotEmpty) {
|
|
_userInfoFutures.remove(userId);
|
|
_userInfoFailedAt.remove(userId);
|
|
}
|
|
widget.msg.needUpDataUserInfo = false;
|
|
}
|
|
if (userId == null || userId.isEmpty) {
|
|
return _buildPendingUserInfoLine(context);
|
|
}
|
|
return SCRoomUtils.roomUsersMap[userId] == null
|
|
? FutureBuilder<SocialChatUserProfile?>(
|
|
future: _loadRoomUserInfoSilently(userId),
|
|
builder: (ct, AsyncSnapshot<SocialChatUserProfile?> snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.done) {
|
|
if (snapshot.hasData) {
|
|
SocialChatUserProfile user = snapshot.data!;
|
|
SCRoomUtils.roomUsersMap[user.id ?? ""] = user;
|
|
return _buildNewUserInfoLine(context, user);
|
|
}
|
|
}
|
|
return _buildPendingUserInfoLine(context);
|
|
},
|
|
)
|
|
: _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
|
|
}
|
|
|
|
Future<SocialChatUserProfile?> _loadRoomUserInfoSilently(String userId) {
|
|
final failedAt = _userInfoFailedAt[userId];
|
|
if (failedAt != null &&
|
|
DateTime.now().difference(failedAt) < _userInfoFailureRetryInterval) {
|
|
return Future.value(null);
|
|
}
|
|
return _userInfoFutures.putIfAbsent(userId, () async {
|
|
try {
|
|
final user = await SCAccountRepository().loadUserInfo(
|
|
userId,
|
|
silentErrorToast: true,
|
|
);
|
|
SCRoomUtils.roomUsersMap[user.id ?? userId] = user;
|
|
_userInfoFailedAt.remove(userId);
|
|
return user;
|
|
} catch (error) {
|
|
_userInfoFailedAt[userId] = DateTime.now();
|
|
_userInfoFutures.remove(userId);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
Widget _buildPendingUserInfoLine(BuildContext context) {
|
|
return Row(
|
|
children: [
|
|
GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () {
|
|
widget.onClick(widget.msg.user);
|
|
},
|
|
child: Stack(
|
|
alignment: AlignmentDirectional.bottomEnd,
|
|
children: [
|
|
head(
|
|
url: widget.msg.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),
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 76.w,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildUserNameAndLevels(widget.msg.user),
|
|
SizedBox(height: 2.w),
|
|
_buildLongMedals(
|
|
userId: widget.msg.user?.id,
|
|
fallbackBadges: _activeWearBadges(widget.msg.user),
|
|
),
|
|
SizedBox(height: 2.w),
|
|
_buildMedals(
|
|
userId: widget.msg.user?.id,
|
|
fallbackBadges: _activeWearBadges(widget.msg.user),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
_buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) {
|
|
return Row(
|
|
children: [
|
|
GestureDetector(
|
|
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),
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 76.w,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildUserNameAndLevels(user),
|
|
SizedBox(height: 2.w),
|
|
_buildLongMedals(
|
|
userId: user.id,
|
|
fallbackBadges: _activeWearBadges(user),
|
|
),
|
|
SizedBox(height: 2.w),
|
|
_buildMedals(
|
|
userId: user.id,
|
|
fallbackBadges: _activeWearBadges(user),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
///消息
|
|
class Msg {
|
|
String? groupId;
|
|
String? msg;
|
|
MicRes? userWheat;
|
|
String? type;
|
|
String? noticeAction;
|
|
SocialChatUserProfile? user;
|
|
String? role = "";
|
|
String? rocketIconUrl = "";
|
|
SocialChatGiftRes? gift;
|
|
|
|
//礼物个数
|
|
num? number;
|
|
|
|
// 自定义动画需要播放的次数
|
|
int? customAnimationCount;
|
|
|
|
num? awardAmount;
|
|
|
|
// 送礼给谁
|
|
SocialChatUserProfile? toUser;
|
|
List<String>? targetUserIds;
|
|
String? giftBatchId;
|
|
int? time = 0;
|
|
|
|
//动画是否已经播放(骰子、石头剪刀布、随机数字)
|
|
bool? hasPlayedAnimation = false;
|
|
|
|
bool? needUpDataUserInfo = false;
|
|
|
|
Msg({
|
|
required this.groupId,
|
|
required this.msg,
|
|
required this.type,
|
|
this.noticeAction,
|
|
this.user,
|
|
this.toUser,
|
|
this.userWheat,
|
|
this.role = "",
|
|
this.rocketIconUrl,
|
|
this.gift,
|
|
this.number,
|
|
this.customAnimationCount,
|
|
this.awardAmount,
|
|
this.targetUserIds,
|
|
this.giftBatchId,
|
|
this.hasPlayedAnimation,
|
|
this.needUpDataUserInfo,
|
|
}) {
|
|
time = DateTime.now().millisecondsSinceEpoch;
|
|
}
|
|
|
|
Msg.fromJson(dynamic json) {
|
|
groupId = json['groupId'];
|
|
msg = json['msg'];
|
|
type = json['type'];
|
|
noticeAction = json['noticeAction']?.toString();
|
|
role = json['role'];
|
|
rocketIconUrl = json['rocketIconUrl']?.toString();
|
|
time = json['time'];
|
|
number = json['number'];
|
|
customAnimationCount = json['customAnimationCount'];
|
|
awardAmount = json['awardAmount'];
|
|
giftBatchId = json['giftBatchId']?.toString();
|
|
final dynamic rawTargetUserIds = json['targetUserIds'];
|
|
if (rawTargetUserIds is List) {
|
|
targetUserIds =
|
|
rawTargetUserIds
|
|
.map((item) => item?.toString().trim() ?? "")
|
|
.where((id) => id.isNotEmpty)
|
|
.toList();
|
|
}
|
|
hasPlayedAnimation = json['hasPlayedAnimation'];
|
|
needUpDataUserInfo = json['needUpDataUserInfo'];
|
|
userWheat =
|
|
json['userWheat'] != null ? MicRes.fromJson(json['userWheat']) : null;
|
|
user =
|
|
json['user'] != null
|
|
? SocialChatUserProfile.fromJson(json['user'])
|
|
: null;
|
|
toUser =
|
|
json['toUser'] != null
|
|
? SocialChatUserProfile.fromJson(json['toUser'])
|
|
: null;
|
|
|
|
gift =
|
|
json['gift'] != null ? SocialChatGiftRes.fromJson(json['gift']) : null;
|
|
_hydrateRocketRewardPayload(json);
|
|
}
|
|
|
|
void _hydrateRocketRewardPayload(dynamic json) {
|
|
if (!SCRoomMsgType.isRocketRewardUserType(type)) {
|
|
return;
|
|
}
|
|
final root = _stringKeyMap(json);
|
|
final payload = _stringKeyMap(root['data']);
|
|
final source = payload.isEmpty ? root : payload;
|
|
groupId = _firstNonBlankString([
|
|
groupId,
|
|
source['groupId'],
|
|
source['roomAccount'],
|
|
source['room_account'],
|
|
source['roomId'],
|
|
source['room_id'],
|
|
]);
|
|
role = _firstNonBlankString([
|
|
role,
|
|
source['rewardType'],
|
|
source['reward_type'],
|
|
]);
|
|
rocketIconUrl = _firstNonBlankString([
|
|
rocketIconUrl,
|
|
source['rocketIconUrl'],
|
|
source['rocket_icon_url'],
|
|
source['rocketIcon'],
|
|
source['rewardCover'],
|
|
source['reward_cover'],
|
|
]);
|
|
msg = _firstNonBlankString([
|
|
msg,
|
|
source['rewardCover'],
|
|
source['reward_cover'],
|
|
source['rewardIconUrl'],
|
|
source['reward_icon_url'],
|
|
source['cover'],
|
|
rocketIconUrl,
|
|
]);
|
|
user ??= _rocketRewardUserProfile(source);
|
|
}
|
|
|
|
static SocialChatUserProfile? _rocketRewardUserProfile(
|
|
Map<String, dynamic> source,
|
|
) {
|
|
final userId = _firstNonBlankString([
|
|
source['userId'],
|
|
source['user_id'],
|
|
source['rewardUserId'],
|
|
source['reward_user_id'],
|
|
source['receiverUserId'],
|
|
source['receiver_user_id'],
|
|
source['targetUserId'],
|
|
source['target_user_id'],
|
|
]);
|
|
final nickname = _firstNonBlankString([
|
|
source['userNickname'],
|
|
source['user_nickname'],
|
|
source['nickname'],
|
|
source['receiverNickname'],
|
|
source['receiver_nickname'],
|
|
source['account'],
|
|
userId,
|
|
]);
|
|
final account = _firstNonBlankString([
|
|
source['account'],
|
|
source['userAccount'],
|
|
source['user_account'],
|
|
source['actualAccount'],
|
|
source['actual_account'],
|
|
userId,
|
|
]);
|
|
final avatar = _firstNonBlankString([
|
|
source['userAvatar'],
|
|
source['user_avatar'],
|
|
source['avatar'],
|
|
source['receiverAvatar'],
|
|
source['receiver_avatar'],
|
|
]);
|
|
if (userId.isEmpty &&
|
|
nickname.isEmpty &&
|
|
account.isEmpty &&
|
|
avatar.isEmpty) {
|
|
return null;
|
|
}
|
|
return SocialChatUserProfile(
|
|
id: userId,
|
|
account: account,
|
|
userNickname: nickname,
|
|
userAvatar: avatar,
|
|
);
|
|
}
|
|
|
|
static Map<String, dynamic> _stringKeyMap(dynamic value) {
|
|
if (value is Map<String, dynamic>) {
|
|
return value;
|
|
}
|
|
if (value is Map) {
|
|
return value.map((key, item) => MapEntry(key.toString(), item));
|
|
}
|
|
return const <String, dynamic>{};
|
|
}
|
|
|
|
static String _firstNonBlankString(Iterable<dynamic> values) {
|
|
for (final value in values) {
|
|
final text = value?.toString().trim() ?? '';
|
|
if (text.isNotEmpty && text.toLowerCase() != 'null') {
|
|
return text;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
final map = <String, dynamic>{};
|
|
map['groupId'] = groupId;
|
|
map['msg'] = msg;
|
|
map['type'] = type;
|
|
if ((noticeAction ?? "").trim().isNotEmpty) {
|
|
map['noticeAction'] = noticeAction;
|
|
}
|
|
map['role'] = role;
|
|
if ((rocketIconUrl ?? "").trim().isNotEmpty) {
|
|
map['rocketIconUrl'] = rocketIconUrl;
|
|
}
|
|
map['time'] = time;
|
|
map['awardAmount'] = awardAmount;
|
|
map['number'] = number;
|
|
map['customAnimationCount'] = customAnimationCount;
|
|
map['hasPlayedAnimation'] = hasPlayedAnimation;
|
|
map['needUpDataUserInfo'] = needUpDataUserInfo;
|
|
if ((giftBatchId ?? "").trim().isNotEmpty) {
|
|
map['giftBatchId'] = giftBatchId;
|
|
}
|
|
if (targetUserIds != null) {
|
|
map['targetUserIds'] = targetUserIds;
|
|
}
|
|
if (userWheat != null) {
|
|
map['userWheat'] = userWheat?.toJson();
|
|
}
|
|
if (user != null) {
|
|
map['user'] = user?.toJson();
|
|
}
|
|
if (toUser != null) {
|
|
map['toUser'] = toUser?.toJson();
|
|
}
|
|
if (gift != null) {
|
|
map['gift'] = gift?.toJson();
|
|
}
|
|
return map;
|
|
}
|
|
}
|