import 'package:flutter/material.dart'; import 'package:flutter_openim_sdk/flutter_openim_sdk.dart'; import 'package:get/get.dart'; import 'package:openim_common/openim_common.dart'; import '../../widgets/file_download_progress.dart'; import 'chat_logic.dart'; class ChatPage extends StatelessWidget { final logic = Get.find(tag: GetTags.chat); ChatPage({super.key}); Widget _buildItemView(Message message) => ChatItemView( key: logic.itemKey(message), message: message, textScaleFactor: logic.scaleFactor.value, allAtMap: logic.getAtMapping(message), timelineStr: logic.getShowTime(message), sendStatusSubject: logic.sendStatusSub, closePopMenuSubject: logic.forceCloseMenuSub, enabledReadStatus: logic.enabledReadStatus(message), readingDuration: logic.readTime(message), isPlayingSound: logic.isPlaySound(message), showLongPressMenu: !logic.isInvalidGroup, leftNickname: logic.getNewestNickname(message), leftFaceUrl: logic.getNewestFaceURL(message), rightNickname: logic.senderName, rightFaceUrl: OpenIM.iMManager.userInfo.faceURL, showLeftNickname: !logic.isSingleChat, showRightNickname: !logic.isSingleChat, enabledCopyMenu: logic.showCopyMenu(message), enabledRevokeMenu: logic.showRevokeMenu(message), enabledReplyMenu: logic.showReplyMenu(message), enabledForwardMenu: logic.showForwardMenu(message), enabledDelMenu: logic.showDelMenu(message), enabledAddEmojiMenu: logic.showAddEmojiMenu(message), onFailedToResend: () => logic.failedResend(message), onPopMenuShowChanged: logic.onPopMenuShowChanged, onClickItemView: () => logic.parseClickEvent(message), onTapCopyMenu: () => logic.copy(message), onTapDelMenu: () => logic.deleteMsg(message), onTapForwardMenu: () => logic.forward(message), onTapRevokeMenu: () { logic.markRevokedMessage(message); logic.revokeMsgV2(message); }, onTapAddEmojiMenu: () => logic.addEmoji(message), visibilityChange: (msg, visible) { logic.markMessageAsRead(message, visible); }, onLongPressLeftAvatar: () { logic.onLongPressLeftAvatar(message); }, onLongPressRightAvatar: () {}, onTapLeftAvatar: () { logic.onTapLeftAvatar(message); }, onVisibleTrulyText: (text) { logic.copyTextMap[message.clientMsgID] = text; }, customTypeBuilder: _buildCustomTypeItemView, fileDownloadProgressView: FileDownloadProgressView(message), patterns: [ MatchPattern( type: PatternType.email, onTap: logic.clickLinkText, ), MatchPattern( type: PatternType.url, onTap: logic.clickLinkText, ), MatchPattern( type: PatternType.mobile, onTap: logic.clickLinkText, ), MatchPattern( type: PatternType.tel, onTap: logic.clickLinkText, ), ], mediaItemBuilder: (context, message) { return _buildMediaItem(context, message); }, onTapUserProfile: handleUserProfileTap, ); void handleUserProfileTap(({String userID, String name, String? faceURL, String? groupID}) userProfile) { final userInfo = UserInfo(userID: userProfile.userID, nickname: userProfile.name, faceURL: userProfile.faceURL); logic.viewUserInfo(userInfo); } Widget? _buildMediaItem(BuildContext context, Message message) { if (message.contentType != MessageType.picture && message.contentType != MessageType.video) { return null; } return GestureDetector( onTap: () async { try { logic.stopVoice(); IMUtils.previewMediaFile( context: context, message: message, onAutoPlay: (index) { return !logic.playOnce; }, muted: logic.rtcIsBusy, onPageChanged: (index) { logic.playOnce = true; }, onOperate: (type) { if (type == OperateType.forward) { logic.forward(message); } }).then((value) { logic.playOnce = false; }); } catch (e) { IMViews.showToast(e.toString()); } }, child: Hero( tag: message.clientMsgID!, child: _buildMediaContent(message), placeholderBuilder: (BuildContext context, Size heroSize, Widget child) => child, ), ); } Widget _buildMediaContent(Message message) { final isOutgoing = message.sendID == OpenIM.iMManager.userID; if (message.isVideoType) { return ChatVideoView( isISend: isOutgoing, message: message, ); } else { return ChatPictureView( isISend: isOutgoing, message: message, ); } } CustomTypeInfo? _buildCustomTypeItemView(_, Message message) { final data = IMUtils.parseCustomMessage(message); if (null != data) { final viewType = data['viewType']; if (viewType == CustomMessageType.call) { final type = data['type']; final content = data['content']; final view = ChatCallItemView(type: type, content: content); return CustomTypeInfo(view); } else if (viewType == CustomMessageType.deletedByFriend || viewType == CustomMessageType.blockedByFriend) { final view = ChatFriendRelationshipAbnormalHintView( name: logic.nickname.value, onTap: logic.sendFriendVerification, blockedByFriend: viewType == CustomMessageType.blockedByFriend, deletedByFriend: viewType == CustomMessageType.deletedByFriend, ); return CustomTypeInfo(view, false, false); } else if (viewType == CustomMessageType.removedFromGroup) { return CustomTypeInfo( StrRes.removedFromGroupHint.toText..style = Styles.ts_8E9AB0_12sp, false, false, ); } else if (viewType == CustomMessageType.groupDisbanded) { return CustomTypeInfo( StrRes.groupDisbanded.toText..style = Styles.ts_8E9AB0_12sp, false, false, ); } else if (viewType == CustomMessageType.tag) { final isISend = message.sendID == OpenIM.iMManager.userID; if (null != data['textElem']) { final textElem = TextElem.fromJson(data['textElem']); return CustomTypeInfo( ChatText( text: textElem.content ?? '', textScaleFactor: logic.scaleFactor.value, model: TextModel.normal, ), ); } else if (null != data['soundElem']) { final soundElem = SoundElem.fromJson(data['soundElem']); return CustomTypeInfo( ChatVoiceView( isISend: isISend, soundPath: soundElem.soundPath, soundUrl: soundElem.sourceUrl, duration: soundElem.duration, isPlaying: logic.isPlaySound(message), ), ); } } } return null; } Widget? get _groupCallHintView => null; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: logic.willPop(), child: ChatVoiceRecordLayout( onCompleted: logic.sendVoice, builder: (bar) => Obx(() { return Scaffold( backgroundColor: Styles.c_F0F2F6, appBar: TitleBar.chat( title: logic.nickname.value, member: logic.memberStr, subTitle: logic.subTile, showOnlineStatus: logic.showOnlineStatus(), isOnline: logic.onlineStatus.value, onCloseMultiModel: logic.exit, onClickMoreBtn: logic.chatSetup, onClickCallBtn: logic.call, ), body: SafeArea( child: WaterMarkBgView( text: '', path: logic.background.value, backgroundColor: Styles.c_FFFFFF, floatView: _groupCallHintView, bottomView: ChatInputBox( forceCloseToolboxSub: logic.forceCloseToolbox, controller: logic.inputCtrl, focusNode: logic.focusNode, isNotInGroup: logic.isInvalidGroup, directionalText: logic.directionalText(), onCloseDirectional: logic.onClearDirectional, onSend: (v) => logic.sendTextMsg(), toolbox: ChatToolBox( onTapAlbum: logic.onTapAlbum, onTapCamera: logic.onTapCamera, onTapCall: logic.call, onTapCard: logic.onTapCarte, onTapFile: logic.onTapFile, onTapLocation: logic.onTapLocation, ), voiceRecordBar: bar, emojiView: ChatEmojiView( textEditingController: logic.inputCtrl, favoriteList: logic.cacheLogic.urlList, onAddFavorite: logic.favoriteManage, onSelectedFavorite: logic.sendFavoritePic, ), ), child: ChatListView( onTouch: () => logic.closeToolbox(), itemCount: logic.messageList.length, controller: logic.scrollController, onScrollToBottomLoad: logic.onScrollToBottomLoad, onScrollToTop: logic.onScrollToTop, itemBuilder: (_, index) { final message = logic.indexOfMessage(index); return Obx(() => _buildItemView(message)); }, ), ), )); }), ), ); } }