This commit is contained in:
2025-06-30 16:24:20 +08:00
commit 68de5eb048
454 changed files with 56921 additions and 0 deletions
@@ -0,0 +1,36 @@
List<Map<String, Object>> messageReactionEmojiData = [
{"name": "red heart", "unicode": 10084},
{"name": "OK", "unicode": 128076},
{"name": "thumbs up", "unicode": 128077},
{"name": "flexed biceps", "unicode": 128170},
{"name": "waving hand", "unicode": 128079},
{"name": "handshake", "unicode": 129309},
{"name": "folded hands", "unicode": 128591},
{"name": "raised fist", "unicode": 9994},
{"name": "thumbs down", "unicode": 128078},
{"name": "smiling face with sunglasses", "unicode": 128526},
{"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
{"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
{"name": "smiling face with hearts", "unicode": 129392},
{"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
{"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
{"name": "flushed face", "unicode": 128563},
{"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
{"name": "FACE WITH COLD SWEAT", "unicode": 128531},
{"name": "loudly crying face", "unicode": 128557},
{"name": "CONFOUNDED FACE", "unicode": 128534},
{"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
{
"name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
"unicode": 128541
},
{"name": "DISAPPOINTED FACE", "unicode": 128542},
{"name": "ANGRY FACE", "unicode": 128544},
{"name": "POUTING FACE", "unicode": 128545},
{"name": "CRYING FACE", "unicode": 128546},
{"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
{"name": "FEARFUL FACE", "unicode": 128552},
{"name": "SLEEPY FACE", "unicode": 128554},
{"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
{"name": "FACE WITH OK GESTURE", "unicode": 128582},
];
@@ -0,0 +1,181 @@
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
class TIMUIKitMessageReactionDetail extends StatefulWidget {
/// the index of the current emoji sticker
final int currentStickerIndex;
/// the list of member
final List<V2TimGroupMemberFullInfo?>? memberList;
/// message reaction map
final Map<String, dynamic> messageReaction;
/// the sticker list from message reaction
final List<int> stickerList;
final Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
const TIMUIKitMessageReactionDetail(
{required this.currentStickerIndex,
this.memberList,
required this.messageReaction,
Key? key,
required this.stickerList,
this.onTapAvatar})
: super(key: key);
@override
State<StatefulWidget> createState() => TIMUIKitMessageReactionDetailState();
}
class TIMUIKitMessageReactionDetailState extends TIMUIKitState<TIMUIKitMessageReactionDetail>
with TickerProviderStateMixin {
final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
Widget getUserItem(String userID, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
V2TimGroupMemberFullInfo? memberInfo;
String showName = userID;
try {
memberInfo = widget.memberList?.firstWhere((element) => element?.userID == userID);
if (memberInfo != null) {
if (memberInfo.friendRemark != null && memberInfo.friendRemark!.isNotEmpty) {
showName = memberInfo.friendRemark!;
} else if (memberInfo.nameCard != null && memberInfo.nameCard!.isNotEmpty) {
showName = memberInfo.nameCard!;
} else if (memberInfo.nickName != null && memberInfo.nickName!.isNotEmpty) {
showName = memberInfo.nickName!;
} else {
showName = memberInfo.userID;
}
}
} catch (e) {
// e
}
return InkWell(
onTapDown: (tapDetails) {
if (onTapAvatar != null) {
if (userID != selfInfoModel.loginInfo?.userID) {
onTapAvatar(userID, tapDetails);
}
}
},
child: Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
child: Row(
children: [
Container(
margin: const EdgeInsets.only(right: 12),
child: SizedBox(
height: 40,
width: 40,
child: (memberInfo?.faceUrl != null)
? Avatar(faceUrl: memberInfo!.faceUrl!, showName: userID)
: Container(),
),
),
Expanded(
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(top: 20, bottom: 20, right: 28),
child: Text(
showName,
style: TextStyle(color: theme.black, fontSize: 18),
),
)),
],
),
),
);
}
Widget stickerItem(int sticker, int length) {
return Container(
margin: const EdgeInsets.only(top: 12, bottom: 12),
padding: const EdgeInsets.only(left: 10, right: 10, top: 2, bottom: 1),
decoration: const BoxDecoration(
color: Color(0x198a8a8a),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.only(bottom: 3),
child: Text(
String.fromCharCode(sticker),
style: const TextStyle(
fontSize: 16,
),
),
),
Container(
margin: const EdgeInsets.only(left: 6),
child: Text(length.toString()),
),
],
),
);
}
Widget getStickerNameList(
int sticker, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
final nameList = widget.messageReaction[sticker.toString()];
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [...nameList.map((e) => getUserItem(e, theme, onTapAvatar))],
),
);
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
return DefaultTabController(
initialIndex: widget.currentStickerIndex,
length: widget.stickerList.length,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(bottom: 4),
child: TabBar(
isScrollable: true,
labelColor: theme.primaryColor,
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
unselectedLabelColor: hexToColor("62626b"),
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal),
indicatorSize: TabBarIndicatorSize.label,
indicatorColor: theme.primaryColor ?? hexToColor("62626b"),
tabs: [
...widget.stickerList.map((element) {
return stickerItem(element, widget.messageReaction[element.toString()].length);
})
],
),
),
Expanded(
child: TabBarView(
children: widget.stickerList
.map((int sticker) => getStickerNameList(sticker, theme, widget.onTapAvatar))
.toList()))
],
),
));
}
}
@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/message_reaction_emoji.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart'
as emoji;
import 'package:tencent_cloud_chat_uikit/ui/widgets/emoji.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/extended_wrap/extended_wrap.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
enum SelectEmojiPanelPosition { up, down }
class TIMUIKitMessageReactionEmojiSelectPanel extends StatefulWidget {
final ValueChanged<int> onSelect;
final bool isShowMoreSticker;
final ValueChanged<bool> onClickShowMore;
const TIMUIKitMessageReactionEmojiSelectPanel(
{Key? key,
required this.onSelect,
required this.isShowMoreSticker,
required this.onClickShowMore})
: super(key: key);
@override
State<StatefulWidget> createState() =>
TIMUIKitMessageReactionEmojiSelectPanelState();
}
class TIMUIKitMessageReactionEmojiSelectPanelState
extends TIMUIKitState<TIMUIKitMessageReactionEmojiSelectPanel> {
bool isShowMore = false;
_buildSimplePanel(TUITheme theme) {
final List<Map<String, Object>> emojiData = messageReactionEmojiData;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
return Material(
color: Colors.white,
child: ExtendedWrap(
maxLines: widget.isShowMoreSticker ? 5 : 1,
spacing: 18,
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 24,
children: [
if(!isDesktopScreen)
GestureDetector(
onTap: () {
widget.onClickShowMore(!widget.isShowMoreSticker);
},
child: SizedBox(
height: 34,
child: Icon(
widget.isShowMoreSticker
? Icons.cancel_outlined
: Icons.add_circle_outline_outlined,
color: hexToColor("444444"),
size: 26),
),
),
...emojiData.map(
(e) {
var item = Emoji.fromJson(e);
return SizedBox(
// width: 50,
child: InkWell(
splashColor: Colors.white,
onTap: () {
widget.onSelect(item.unicode);
},
child: emoji.EmojiItem(
name: item.name,
unicode: item.unicode,
),
),
);
},
).toList()
],
),
);
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
return Container(
child: _buildSimplePanel(theme),
);
}
}
@@ -0,0 +1,198 @@
// ignore_for_file: unused_field
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_change_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/extended_wrap/extended_wrap.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
class TIMUIKitMessageReactionShowItem extends TIMUIKitStatelessWidget {
/// the unicode of the emoji
final int sticker;
/// the list contains the name who choose the current emoji
final List nameList;
/// current message
final V2TimMessage message;
/// show the details of message reaction
final Function(int sticker) onShowDetail;
/// the member in current chat
final List<V2TimGroupMemberFullInfo?> memberList;
TIMUIKitMessageReactionShowItem(
{required this.message,
required this.sticker,
required this.memberList,
required this.onShowDetail,
required this.nameList,
Key? key})
: super(key: key);
final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
final MessageService _messageService = serviceLocator<MessageService>();
clickOnCurrentSticker() async {
for (int i = 0; i < 5; i++) {
final res = await modifySticker();
if (res.code == 0) {
break;
}
}
}
Future<V2TimValueCallback<V2TimMessageChangeInfo>> modifySticker() async {
return await Future.delayed(const Duration(milliseconds: 50), () async {
return await MessageReactionUtils.clickOnSticker(message, sticker);
});
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
final option1 = nameList.length;
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context);
final List<String> userIDs = [];
for (final user in nameList) {
final V2TimGroupMemberFullInfo? memberInfo =
memberList.firstWhereOrNull((element) => element?.userID == user && TencentUtils.checkString(user) != null);
if ((memberInfo == null || TencentUtils.checkString(memberInfo.userID) == null) &&
TencentUtils.checkString(user.toString()) != null) {
userIDs.add(user.toString());
}
}
if (userIDs.isNotEmpty) {
model.getUserShowName(userIDs);
}
return LayoutBuilder(builder: (context, constraints) {
return Container(
padding: const EdgeInsets.only(
left: 10,
right: 10,
),
decoration: const BoxDecoration(
color: Color(0x19727271),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: clickOnCurrentSticker,
child: Container(
margin:
EdgeInsets.only(bottom: (!PlatformUtils().isIOS) ? 4 : 2, top: (!PlatformUtils().isIOS) ? 4 : 0),
child: Text(
String.fromCharCode(sticker),
style: TextStyle(fontSize: (!PlatformUtils().isIOS) ? 12 : 16, color: hexToColor("f9453d")),
),
),
),
Container(
margin: const EdgeInsets.only(left: 6, right: 6),
child: SizedBox(
width: 1,
height: 14,
child: DecoratedBox(
decoration: BoxDecoration(color: theme.weakTextColor),
),
),
),
Container(
constraints: BoxConstraints(
maxWidth: constraints.maxWidth * 0.8,
),
child: ExtendedWrap(
maxLines: 1,
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
overflowWidget: GestureDetector(
onTap: () {
onShowDetail(sticker);
},
child: Text(
TIM_t_para("...共{{option1}}人", "...共$option1人")(option1: option1),
style: TextStyle(fontSize: 12, color: hexToColor("616669")),
),
),
children: [
...nameList.map((e) {
String showName = e;
if (memberList.isNotEmpty) {
try {
final V2TimGroupMemberFullInfo? memberInfo =
memberList.firstWhere((element) => element?.userID == e);
if (memberInfo != null) {
if (memberInfo.friendRemark != null && memberInfo.friendRemark!.isNotEmpty) {
showName = memberInfo.friendRemark!;
} else if (memberInfo.nameCard != null && memberInfo.nameCard!.isNotEmpty) {
showName = memberInfo.nameCard!;
} else if (memberInfo.nickName != null && memberInfo.nickName!.isNotEmpty) {
showName = memberInfo.nickName!;
} else {
showName = memberInfo.userID;
}
} else {
final String? data = model.groupUserShowName[e];
if (TencentUtils.checkString(data) != null) {
showName = data ?? e;
}
}
} catch (error) {
final String? data = model.groupUserShowName[e];
if (TencentUtils.checkString(data) != null) {
showName = data ?? e;
}
}
} else {
final String? data = model.groupUserShowName[e];
if (TencentUtils.checkString(data) != null) {
showName = data ?? e;
}
}
return InkWell(
onTapDown: (tapDetails) {
if (model.onTapAvatar != null) {
if (e != selfInfoModel.loginInfo?.userID) {
model.onTapAvatar!(e, tapDetails);
}
}
},
child: Text(
showName,
style: TextStyle(fontSize: 12, color: hexToColor("616669")),
),
);
})
],
),
)
],
),
);
});
}
}
@@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_detail.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_item.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
class TIMUIKitMessageReactionShowPanel extends TIMUIKitStatelessWidget {
/// current message
final V2TimMessage message;
TIMUIKitMessageReactionShowPanel({required this.message, Key? key}) : super(key: key);
final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
void showMore(BuildContext context, List<V2TimGroupMemberFullInfo?>? memberList, Map<String, dynamic> messageReaction,
int currentSticker, List<int> stickerList, TUIChatSeparateViewModel model) async {
_showCustomModalBottomSheet(context, memberList, messageReaction, currentSticker, stickerList, model);
}
Future<Future<int?>> _showCustomModalBottomSheet(
context,
List<V2TimGroupMemberFullInfo?>? memberList,
Map<String, dynamic> messageReaction,
int currentSticker,
List<int> stickerList,
TUIChatSeparateViewModel model) async {
return showModalBottomSheet<int>(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.66,
minHeight: MediaQuery.of(context).size.height * 0.2,
),
child: Column(children: [
SizedBox(
height: 50,
child: Stack(
textDirection: TextDirection.rtl,
children: [
Center(
child: Text(
TIM_t("回应详情"),
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () {
Navigator.of(context).pop();
}),
],
),
),
const Divider(height: 1.0),
Expanded(
child: TIMUIKitMessageReactionDetail(
onTapAvatar: model.onTapAvatar,
stickerList: stickerList,
currentStickerIndex: stickerList.indexWhere((element) => element == currentSticker),
memberList: memberList,
messageReaction: messageReaction,
)),
]),
);
},
);
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
Map<String, dynamic> messageReaction = {};
CloudCustomData messageCloudCustomData = MessageReactionUtils.getCloudCustomData(message);
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context);
if (messageCloudCustomData.messageReaction != null && messageCloudCustomData.messageReaction!.isNotEmpty) {
messageReaction = messageCloudCustomData.messageReaction!;
} else {
return const SizedBox(width: 0, height: 0);
}
final List<int> messageReactionStickerList = [];
messageReaction.forEach((key, value) {
messageReactionStickerList.add(int.parse(key));
});
final filteredMessageReactionStickerList = messageReactionStickerList.where((sticker) {
if (messageReaction[sticker.toString()] == null ||
messageReaction[sticker.toString()] is! List ||
messageReaction[sticker.toString()].length == 0) {
return false;
}
return true;
}).toList();
final ConvType convType = model.conversationType ?? ConvType.c2c;
List<V2TimGroupMemberFullInfo?> memberList = [];
if (convType == ConvType.group) {
memberList = model.groupMemberList ?? [];
} else {
final V2TimGroupMemberFullInfo selfInfo = V2TimGroupMemberFullInfo(
userID: selfInfoModel.loginInfo?.userID ?? "",
nickName: selfInfoModel.loginInfo?.nickName,
faceUrl: selfInfoModel.loginInfo?.faceUrl,
);
final V2TimGroupMemberFullInfo targetInfo = V2TimGroupMemberFullInfo(
userID: model.conversationID,
);
memberList = [selfInfo, model.currentChatUserInfo ?? targetInfo];
}
return filteredMessageReactionStickerList.isNotEmpty
? Container(
margin: const EdgeInsets.only(top: 12),
child: Wrap(
spacing: 8,
runSpacing: (!PlatformUtils().isIOS) ? 12 : 8,
children: [
...filteredMessageReactionStickerList.map((sticker) {
return TIMUIKitMessageReactionShowItem(
memberList: memberList,
message: message,
nameList: messageReaction[sticker.toString()],
sticker: sticker,
onShowDetail: (int sticker) {
showMore(
context, memberList, messageReaction, sticker, filteredMessageReactionStickerList, model);
});
}).toList(),
],
),
)
: const SizedBox(width: 0, height: 0);
}
}
@@ -0,0 +1,56 @@
import 'dart:convert';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_change_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
class MessageReactionUtils {
static final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
static final MessageService _messageService = serviceLocator<MessageService>();
static CloudCustomData getCloudCustomData(V2TimMessage message) {
CloudCustomData messageCloudCustomData;
try {
messageCloudCustomData = CloudCustomData.fromJson(
json.decode(TencentUtils.checkString(message.cloudCustomData) != null ? message.cloudCustomData! : "{}"));
} catch (e) {
messageCloudCustomData = CloudCustomData();
}
return messageCloudCustomData;
}
static Map<String, dynamic> getMessageReaction(V2TimMessage message) {
return getCloudCustomData(message).messageReaction ?? {};
}
static Future<V2TimValueCallback<V2TimMessageChangeInfo>> clickOnSticker(V2TimMessage message, int sticker) async {
final CloudCustomData messageCloudCustomData = getCloudCustomData(message);
final Map<String, dynamic> messageReaction = messageCloudCustomData.messageReaction ?? {};
List targetList = messageReaction["$sticker"] ?? [];
if (targetList.contains(selfInfoModel.loginInfo!.userID!)) {
targetList.remove(selfInfoModel.loginInfo!.userID!);
} else {
targetList = [selfInfoModel.loginInfo!.userID!, ...targetList];
}
messageReaction["$sticker"] = targetList;
if (PlatformUtils().isWeb) {
final decodedMessage = jsonDecode(message.messageFromWeb!);
decodedMessage["cloudCustomData"] = jsonEncode(messageCloudCustomData.toMap());
message.messageFromWeb = jsonEncode(decodedMessage);
} else {
message.cloudCustomData = json.encode(messageCloudCustomData.toMap());
}
return await _messageService.modifyMessage(message: message);
}
}
@@ -0,0 +1,175 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
class TIMUIKitMessageReactionWrapper extends StatefulWidget {
final Widget child;
final V2TimMessage message;
final Color? backgroundColor;
final bool isFromSelf;
final BorderRadius? borderRadius;
final bool isShowJump;
final VoidCallback? clearJump;
final bool isShowMessageReaction;
final TUIChatSeparateViewModel chatModel;
const TIMUIKitMessageReactionWrapper(
{Key? key,
required this.isShowJump,
this.clearJump,
required this.isFromSelf,
this.backgroundColor,
required this.chatModel,
required this.message,
this.borderRadius,
required this.child,
this.isShowMessageReaction = true})
: super(key: key);
@override
State<StatefulWidget> createState() => _TIMUIKitMessageReactionWrapperState();
}
class _TIMUIKitMessageReactionWrapperState extends TIMUIKitState<TIMUIKitMessageReactionWrapper> {
bool isShowJumpState = false;
bool isShining = false;
bool isShowBorder = false;
_showJumpColor() {
if ((widget.chatModel.jumpMsgID != widget.message.msgID) && (widget.message.msgID?.isNotEmpty ?? true)) {
return;
}
isShining = true;
int shineAmount = 6;
setState(() {
isShowJumpState = true;
isShowBorder = true;
});
Timer.periodic(const Duration(milliseconds: 300), (timer) {
if (mounted) {
setState(() {
isShowJumpState = shineAmount.isOdd ? true : false;
isShowBorder = shineAmount.isOdd ? true : false;
});
}
if (shineAmount == 0 || !mounted) {
isShining = false;
timer.cancel();
}
shineAmount--;
});
if (widget.clearJump != null) {
widget.clearJump!();
}
}
bool isHaveMessageReaction() {
Map<String, dynamic> messageReaction = {};
CloudCustomData messageCloudCustomData;
try {
messageCloudCustomData = CloudCustomData.fromJson(json.decode(
TencentUtils.checkString(widget.message.cloudCustomData) != null ? widget.message.cloudCustomData! : "{}"));
} catch (e) {
messageCloudCustomData = CloudCustomData();
}
if (messageCloudCustomData.messageReaction != null && messageCloudCustomData.messageReaction!.isNotEmpty) {
messageReaction = messageCloudCustomData.messageReaction!;
} else {
return false;
}
final List<int> messageReactionStickerList = [];
messageReaction.forEach((key, value) {
messageReactionStickerList.add(int.parse(key));
});
final filteredMessageReactionStickerList = messageReactionStickerList.where((sticker) {
if (messageReaction[sticker.toString()] == null ||
messageReaction[sticker.toString()] is! List ||
messageReaction[sticker.toString()].length == 0) {
return false;
}
return true;
});
if (filteredMessageReactionStickerList.isEmpty) {
return false;
}
return true;
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
final borderRadius = widget.isFromSelf
? const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(2),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10))
: const BorderRadius.only(
topLeft: Radius.circular(2),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10));
if (widget.isShowJump) {
if (!isShining) {
Future.delayed(Duration.zero, () {
_showJumpColor();
});
} else {
if ((widget.chatModel.jumpMsgID == widget.message.msgID) && (widget.message.msgID?.isNotEmpty ?? false)) {
if (widget.clearJump != null) {
widget.clearJump!();
}
}
}
}
final defaultStyle = widget.isFromSelf ? theme.lightPrimaryMaterialColor.shade50 : theme.weakBackgroundColor;
final backgroundColor =
isShowJumpState ? const Color.fromRGBO(245, 166, 35, 1) : (widget.backgroundColor ?? defaultStyle);
if (!widget.isShowMessageReaction || !isHaveMessageReaction()) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
border: Border.all(color: Color.fromRGBO(245, 166, 35, (isShowBorder ? 1 : 0)), width: 2)),
child: widget.child,
);
}
return Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: widget.borderRadius ?? borderRadius,
),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
border: Border.all(color: Color.fromRGBO(245, 166, 35, (isShowBorder ? 1 : 0)), width: 2)),
child: widget.child,
),
if (widget.isShowMessageReaction) TIMUIKitMessageReactionShowPanel(message: widget.message)
],
),
);
}
}