yumi-flutter/lib/ui_kit/widgets/room/room_msg_item.dart
2026-05-09 16:55:08 +08:00

1991 lines
61 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/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);
///消息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) {
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 (widget.msg.type == SCRoomMsgType.rocketRewardUser) {
return _buildRocketRewardMsg(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) {
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 = [];
spans.add(
TextSpan(
text: "${widget.msg.user?.userNickname} ",
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: Image.asset(
"sc_images/room/sc_icon_room_rocket_lv${widget.msg.number}.png",
width: 18.w,
),
),
);
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,
),
);
}
///幸运礼物
_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 _buildMedals(List<WearBadge> wearBadge) {
return wearBadge.isNotEmpty
? Expanded(
child: SingleChildScrollView(
child: SizedBox(
height: 25.w,
child: ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: wearBadge.length,
itemBuilder: (context, index) {
return netImage(
width: 25.w,
height: 25.w,
url: wearBadge[index].selectUrl ?? "",
);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(width: 5.w);
},
),
),
),
)
: Container();
}
int _msgColor(String type) {
switch (type) {
case SCRoomMsgType.joinRoom:
case SCRoomMsgType.systemTips:
case SCRoomMsgType.welcome:
return 0xFFFFFFFF;
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);
}
if (_isLocalAsset(path)) {
return RoomEmojiAssetImage(
key: ValueKey(path),
asset: path,
width: width,
height: height,
);
}
return netImage(url: path, width: width, height: height);
}
bool _isLocalAsset(String path) {
return path.startsWith("assets/") || path.startsWith("sc_images/");
}
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);
debugPrint(
'[RoomChat][UserInfo] load failed userId=$userId error=$error',
);
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: 3.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
getWealthLevel(
widget.msg.user?.wealthLevel ?? 0,
width: 48.w,
height: 23.w,
fontSize: 10.sp,
),
getUserLevel(
widget.msg.user?.charmLevel ?? 0,
width: 48.w,
height: 23.w,
fontSize: 10.sp,
),
SizedBox(width: 3.w),
socialchatNickNameText(
maxWidth: 120.w,
fontWeight: FontWeight.w500,
widget.msg.user?.userNickname ?? "",
fontSize: 13.sp,
type: widget.msg.user?.getVIP()?.name ?? "",
needScroll:
(widget.msg.user?.userNickname?.characters.length ??
0) >
10,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
widget.msg.user?.getVIP() != null
? netImage(
url: widget.msg.user?.getVIP()?.cover ?? "",
width: 25.w,
height: 25.w,
)
: Container(),
_buildMedals(
(widget.msg.user?.wearBadge?.where((item) {
return item.use ?? false;
}).toList() ??
[]),
),
],
),
],
),
),
],
);
}
_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: 3.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
getWealthLevel(
user.wealthLevel ?? 0,
width: 48.w,
height: 23.w,
fontSize: 10.sp,
),
getUserLevel(
user.charmLevel ?? 0,
width: 48.w,
height: 23.w,
fontSize: 10.sp,
),
SizedBox(width: 3.w),
socialchatNickNameText(
maxWidth: 120.w,
fontWeight: FontWeight.w500,
user.userNickname ?? "",
fontSize: 13.sp,
type: user.getVIP()?.name ?? "",
needScroll:
(user.userNickname?.characters.length ?? 0) > 10,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
user.getVIP() != null
? netImage(
url: user.getVIP()?.cover ?? "",
width: 25.w,
height: 25.w,
)
: Container(),
_buildMedals(
user.wearBadge?.where((item) {
return item.use ?? false;
}).toList() ??
[],
),
],
),
],
),
),
],
);
}
}
///消息
class Msg {
String? groupId;
String? msg;
MicRes? userWheat;
String? type;
SocialChatUserProfile? user;
String? role = "";
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.user,
this.toUser,
this.userWheat,
this.role = "",
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'];
role = json['role'];
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;
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['groupId'] = groupId;
map['msg'] = msg;
map['type'] = type;
map['role'] = role;
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;
}
}