From a74025c4c4f44d9bb5db6658b96a8ea1400b0558 Mon Sep 17 00:00:00 2001 From: CYH <13923927013@163.com> Date: Tue, 20 Jun 2023 12:31:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AF=9D=E5=88=97?= =?UTF-8?q?=E8=A1=A8UI=EF=BC=8C=E5=AE=8C=E5=96=84=E8=B5=84=E6=96=99UI?= =?UTF-8?q?=EF=BC=8C=E8=B7=B3=E8=BD=AC=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/login/complete_material/logic.dart | 3 +- .../lib/app/login/complete_material/view.dart | 72 ++- .../tim_ui_kit_conversation_total_unread.dart | 52 ++ .../tim_uikit_conversation.dart | 465 ++++++++++++++++++ .../tim_uikit_conversation_draft_text.dart | 46 ++ .../tim_uikit_conversation_item.dart | 206 ++++++++ .../tim_uikit_conversation_last_msg.dart | 152 ++++++ circle_app/lib/app/msg/view.dart | 10 +- circle_app/lib/app/splash/logic.dart | 2 +- circle_app/lib/network/dio_manager.dart | 1 - .../Flutter/GeneratedPluginRegistrant.swift | 2 + 11 files changed, 964 insertions(+), 47 deletions(-) create mode 100644 circle_app/lib/app/msg/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart create mode 100644 circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation.dart create mode 100644 circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart create mode 100644 circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_item.dart create mode 100644 circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart diff --git a/circle_app/lib/app/login/complete_material/logic.dart b/circle_app/lib/app/login/complete_material/logic.dart index fac26e4..5f8758b 100644 --- a/circle_app/lib/app/login/complete_material/logic.dart +++ b/circle_app/lib/app/login/complete_material/logic.dart @@ -67,6 +67,7 @@ class Complete_materialLogic extends GetxController { _setImageFileListFromFile(pickedFile!); // }); } catch (e) { + print(e); // setState(() { // _pickImageError = e; // }); @@ -141,7 +142,7 @@ class Complete_materialLogic extends GetxController { }); var bean = BaseResponse.fromJson(data, (data) => data); if (bean.code == 200) { - Get.toNamed(AppRoutes.Home); + Get.offAllNamed(AppRoutes.Home); } } diff --git a/circle_app/lib/app/login/complete_material/view.dart b/circle_app/lib/app/login/complete_material/view.dart index 650b06e..edbbfea 100644 --- a/circle_app/lib/app/login/complete_material/view.dart +++ b/circle_app/lib/app/login/complete_material/view.dart @@ -73,7 +73,7 @@ class Complete_materialPage extends StatelessWidget { height: 15.sp, ), Text( - '数据表明95%的人对完整的兽设更感兴趣', + '数据表明95%的人对真实的头像更感兴趣', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w500, @@ -148,13 +148,18 @@ class Complete_materialPage extends StatelessWidget { funcWidget( '兴趣', Row( - children: [interestWdiget('JK圈',controller)], - ), - () async { - controller.numbers = await Get.toNamed(AppRoutes.SelectCircleActivity,arguments: controller.configBean.interestMap); - controller.update(); - }), - + children: controller.numbers.isNotEmpty + ? [interestWdiget('JK圈', controller)] + : [], + ), () async { + var data = await Get.toNamed( + AppRoutes.SelectCircleActivity, + arguments: controller.configBean.interestMap); + if (data != null) { + controller.numbers = data; + controller.update(); + } + }), Container( margin: EdgeInsets.only( top: 24.sp, @@ -194,7 +199,7 @@ class Complete_materialPage extends StatelessWidget { GestureDetector( onTap: () { if (controller.type == "user") { - // Navigator.pop(context); + // Navigator.pop(context); controller.editInfo(); } else { controller.checkInfo(); @@ -285,7 +290,7 @@ class Complete_materialPage extends StatelessWidget { ); } - interestWdiget(String interest,Complete_materialLogic controller) { + interestWdiget(String interest, Complete_materialLogic controller) { return Container( height: 59.sp, width: 240.sp, @@ -300,35 +305,18 @@ class Complete_materialPage extends StatelessWidget { return Container( margin: EdgeInsets.only(right: 11.sp), // 替换为实际的 item 间距 child: Container( + height: 34.sp, + padding: EdgeInsets.only(left: 15.sp, right: 15.sp), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(17.0), // 设置圆角半径 - gradient: LinearGradient( - colors: [ - Color(0xFFffffff), - Color(0xFFffffff), - ], - ), + borderRadius: BorderRadius.circular(17.0.sp), // 设置圆角半径 + border: Border.all(color: Colors.white, width: 1.sp), color: Color(0xFF392D53), ), - child: Container( - margin: EdgeInsets.all(0.2.sp), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(17.0), - // shape: BoxShape.circle, - color: Color(0xFF392D53), - ), - child: Padding( - padding: EdgeInsets.only( - top: 2.sp, bottom: 2.sp, left: 15.sp, right: 15.sp), - child: Center( - child: Text( - controller.numbers[index].name, - style: TextStyle( - fontSize: 12.0, - color: Colors.white, - ), - ), - ), + child: Text( + controller.numbers[index].name, + style: TextStyle( + fontSize: 12.0.sp, + color: Colors.white, ), ), ), // 替换为实际的列表项小部件 @@ -362,7 +350,8 @@ class Complete_materialPage extends StatelessWidget { ? controller.state.sex : '男', onConfirm: (p, position) { controller.state.sex = p; - controller.state.genderId = controller.configBean.genderMap.keys.toList()[position]; + controller.state.genderId = + controller.configBean.genderMap.keys.toList()[position]; controller.update(); }); } @@ -408,7 +397,8 @@ class Complete_materialPage extends StatelessWidget { ? controller.state.role : 'Sado', onConfirm: (p, position) { controller.state.role = p; - controller.state.roleId = controller.configBean.roleMap.keys.toList()[position]; + controller.state.roleId = + controller.configBean.roleMap.keys.toList()[position]; controller.update(); }); } @@ -422,11 +412,9 @@ class Complete_materialPage extends StatelessWidget { ? controller.state.orientation : '异性恋', onConfirm: (p, position) { controller.state.orientation = p; - controller.state.orientationId = controller.configBean.orientationMap.keys.toList()[position]; + controller.state.orientationId = + controller.configBean.orientationMap.keys.toList()[position]; controller.update(); }); } - - - } diff --git a/circle_app/lib/app/msg/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart b/circle_app/lib/app/msg/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart new file mode 100644 index 0000000..7b3abe2 --- /dev/null +++ b/circle_app/lib/app/msg/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart @@ -0,0 +1,52 @@ +// ignore_for_file: camel_case_types + +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.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/view_models/tui_conversation_view_model.dart'; +import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; +import 'package:tencent_cloud_chat_uikit/ui/widgets/unread_message.dart'; + +typedef unreadCountBuilder = Widget Function(int unreadCount); + +class TIMUIKitConversationTotalUnread extends TIMUIKitStatelessWidget { + final TUIConversationViewModel model = + serviceLocator(); + final int? unreadCount; + final unreadCountBuilder? builder; + final double? width; + final double? height; + + TIMUIKitConversationTotalUnread( + {this.width = 22.0, + this.height = 22.0, + this.unreadCount, + this.builder, + Key? key}) + : super(key: key); + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: model), + ], + child: Consumer( + builder: (context, value, child) { + if (value.totalUnReadCount == 0) { + return Container(); + } + + if (builder != null) { + return builder!(value.totalUnReadCount); + } + return UnreadMessage( + unreadCount: unreadCount ?? value.totalUnReadCount, + width: width, + height: height); + }, + ), + ); + } +} diff --git a/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation.dart b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation.dart new file mode 100644 index 0000000..cb31a2e --- /dev/null +++ b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation.dart @@ -0,0 +1,465 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_slidable_for_tencent_im/flutter_slidable.dart'; +import 'package:provider/provider.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; +import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart'; +import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart'; +import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart'; +import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.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/controller/tim_uikit_conversation_controller.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart'; +import 'package:tencent_cloud_chat_uikit/ui/widgets/customize_ball_pulse_header.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; +import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart'; + +import 'tim_uikit_conversation_item.dart'; + +typedef TIMConversationItemBuilder = Widget Function( + V2TimConversation conversationItem, + [V2TimUserStatus? onlineStatus]); + +typedef ConversationItemSlideBuilder = List + Function(V2TimConversation conversationItem); + +typedef ConversationItemSecondaryMenuBuilder = Widget Function( + V2TimConversation conversationItem, VoidCallback onClose); + +class TIMConversation extends StatefulWidget { + /// the callback after clicking conversation item + final ValueChanged? onTapItem; + + /// conversation controller + final TIMUIKitConversationController? controller; + + /// the builder for conversation item + final TIMConversationItemBuilder? itemBuilder; + + /// the builder for Slidable item for each conversation item, shows on narrow screens. + final ConversationItemSlideBuilder? itemSlideBuilder; + + /// the widget of secondary tap menu for each conversation item, shows on wide screens. + final ConversationItemSecondaryMenuBuilder? itemSecondaryMenuBuilder; + + /// the widget shows when no conversation exists + final Widget Function()? emptyBuilder; + + /// the filter for conversation + final bool Function(V2TimConversation? conversation)? conversationCollector; + + /// the builder for the second line in each conservation item, + /// usually shows the summary of the last message + final LastMessageBuilder? lastMessageBuilder; + + /// The life cycle hooks for `TIMUIKitConversation` + final ConversationLifeCycle? lifeCycle; + + /// Control if shows the online status for each user on its avatar. + final bool isShowOnlineStatus; + + /// Control if shows the identifier that the conversation has a draft text, inputted in previous. + /// Also, you have better specifying the `draftText` field for `TIMUIKitChat`, from the `draftText` in `V2TimConversation`, + /// to meet the identifier shows here. + final bool isShowDraft; + + const TIMConversation( + {Key? key, + this.lifeCycle, + this.onTapItem, + this.controller, + this.itemSecondaryMenuBuilder, + this.itemBuilder, + this.isShowDraft = true, + this.itemSlideBuilder, + this.conversationCollector, + this.emptyBuilder, + this.lastMessageBuilder, + this.isShowOnlineStatus = true}) + : super(key: key); + + @override + State createState() { + return _TIMConversationState(); + } +} + +class ConversationItemSlidePanel extends TIMUIKitStatelessWidget { + ConversationItemSlidePanel({ + Key? key, + this.flex = 1, + this.backgroundColor = Colors.white, + this.foregroundColor, + this.autoClose = true, + required this.onPressed, + this.icon, + this.spacing = 4, + this.label, + }) : assert(flex > 0), + assert(icon != null || label != null), + super(key: key); + + /// {@macro slidable.actions.flex} + final int flex; + + /// {@macro slidable.actions.backgroundColor} + final Color backgroundColor; + + /// {@macro slidable.actions.foregroundColor} + final Color? foregroundColor; + + /// {@macro slidable.actions.autoClose} + final bool autoClose; + + /// {@macro slidable.actions.onPressed} + final SlidableActionCallback? onPressed; + + /// An icon to display above the [label]. + final IconData? icon; + + /// The space between [icon] and [label] if both set. + /// + /// Defaults to 4. + final double spacing; + + /// A label to display below the [icon]. + final String? label; + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + return SlidableAction( + onPressed: onPressed, + flex: flex, + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + autoClose: autoClose, + label: label, + spacing: spacing, + ); + } +} + +class _TIMConversationState extends TIMUIKitState { + final TUIConversationViewModel model = + serviceLocator(); + late TIMUIKitConversationController _timuiKitConversationController; + final TUIThemeViewModel themeViewModel = serviceLocator(); + final TUIFriendShipViewModel friendShipViewModel = + serviceLocator(); + late AutoScrollController _autoScrollController; + + @override + void initState() { + super.initState(); + final controller = getController(); + _timuiKitConversationController = controller; + _timuiKitConversationController.model = model; + _autoScrollController = AutoScrollController(); + } + + TIMUIKitConversationController getController() { + return widget.controller ?? TIMUIKitConversationController(); + } + + void onTapConvItem(V2TimConversation conversation) { + if (widget.onTapItem != null) { + widget.onTapItem!(conversation); + } + model.setSelectedConversation(conversation); + } + + _clearHistory(V2TimConversation conversationItem) { + _timuiKitConversationController.clearHistoryMessage( + conversation: conversationItem); + } + + _pinConversation(V2TimConversation conversation) { + _timuiKitConversationController.pinConversation( + conversationID: conversation.conversationID, + isPinned: !conversation.isPinned!); + } + + _deleteConversation(V2TimConversation conversation) { + _timuiKitConversationController.deleteConversation( + conversationID: conversation.conversationID); + } + + List getFilteredConversation() { + List filteredConversationList = model.conversationList + .where( + (element) => (element?.groupID != null || element?.userID != null)) + .toList(); + if (widget.conversationCollector != null) { + filteredConversationList = filteredConversationList + .where(widget.conversationCollector!) + .toList(); + } + return filteredConversationList; + } + + _onScrollToConversation(String conversationID) { + final msgList = getFilteredConversation(); + bool isFound = false; + int targetIndex = 1; + for (int i = msgList.length - 1; i >= 0; i--) { + final currentConversation = msgList[i]; + if (currentConversation?.conversationID == conversationID) { + isFound = true; + targetIndex = i; + break; + } + } + + if (isFound) { + _autoScrollController.scrollToIndex( + targetIndex, + preferPosition: AutoScrollPosition.begin, + ); + } + } + + Widget _defaultSecondaryMenu( + V2TimConversation conversationItem, VoidCallback onClose) { + return TUIKitColumnMenu(data: [ + if (!PlatformUtils().isWeb) + ColumnMenuItem( + label: TIM_t("清除消息"), + icon: const Icon(Icons.clear_all, size: 16), + onClick: () { + onClose(); + _clearHistory(conversationItem); + }), + ColumnMenuItem( + label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"), + icon: Icon( + conversationItem.isPinned! + ? Icons.vertical_align_bottom + : Icons.vertical_align_top, + size: 16), + onClick: () { + onClose(); + _pinConversation(conversationItem); + }), + ColumnMenuItem( + label: TIM_t("删除会话"), + icon: const Icon(Icons.delete_outline, size: 16), + onClick: () { + onClose(); + _deleteConversation(conversationItem); + }), + ]); + } + + List _defaultSlideBuilder( + V2TimConversation conversationItem, + ) { + final theme = themeViewModel.theme; + return [ + if (!PlatformUtils().isWeb) + ConversationItemSlidePanel( + onPressed: (context) { + _clearHistory(conversationItem); + }, + backgroundColor: theme.conversationItemSliderClearBgColor ?? + CommonColor.primaryColor, + foregroundColor: theme.conversationItemSliderTextColor, + label: TIM_t("清除聊天"), + spacing: 0, + autoClose: true, + ), + ConversationItemSlidePanel( + onPressed: (context) { + _pinConversation(conversationItem); + }, + backgroundColor: + theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor, + foregroundColor: theme.conversationItemSliderTextColor, + label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"), + ), + ConversationItemSlidePanel( + onPressed: (context) { + _deleteConversation(conversationItem); + }, + backgroundColor: + theme.conversationItemSliderDeleteBgColor ?? Colors.red, + foregroundColor: theme.conversationItemSliderTextColor, + label: TIM_t("删除"), + ) + ]; + } + + Widget _getSecondaryMenu( + V2TimConversation conversation, VoidCallback onClose) { + if (widget.itemSecondaryMenuBuilder != null) { + return widget.itemSecondaryMenuBuilder!(conversation, onClose); + } + return _defaultSecondaryMenu(conversation, onClose); + } + + ConversationItemSlideBuilder _getSlideBuilder() { + return widget.itemSlideBuilder ?? _defaultSlideBuilder; + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + final theme = value.theme; + final isDesktopScreen = + TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: model), + ChangeNotifierProvider.value(value: friendShipViewModel) + ], + builder: (BuildContext context, Widget? w) { + final _model = Provider.of(context); + bool haveMoreData = _model.haveMoreData; + final _friendShipViewModel = + Provider.of(context); + _model.lifeCycle = widget.lifeCycle; + + List filteredConversationList = + getFilteredConversation(); + + if (TencentUtils.checkString(_model.scrollToConversation) != null) { + _onScrollToConversation(_model.scrollToConversation!); + _model.clearScrollToConversation(); + } + + Widget conversationList() { + return filteredConversationList.isNotEmpty + ? ListView.builder( + controller: _autoScrollController, + shrinkWrap: true, + itemCount: filteredConversationList.length, + itemBuilder: (context, index) { + if (index == filteredConversationList.length - 1) { + if (haveMoreData) { + _timuiKitConversationController.loadData(); + } + } + + final conversationItem = filteredConversationList[index]; + + final V2TimUserStatus? onlineStatus = + _friendShipViewModel.userStatusList.firstWhere( + (item) => item.userID == conversationItem?.userID, + orElse: () => V2TimUserStatus(statusType: 0)); + + if (widget.itemBuilder != null) { + return widget.itemBuilder!( + conversationItem!, onlineStatus); + } + + final slideChildren = + _getSlideBuilder()(conversationItem!); + + final isCurrent = conversationItem.conversationID == + model.selectedConversation?.conversationID; + + final isPined = conversationItem.isPinned ?? false; + + Widget conversationLineItem() { + return Material( + color: Colors.transparent, + child: InkWell( + child: TIMConversationItem( + isCurrent: isCurrent, + isShowDraft: widget.isShowDraft, + lastMessageBuilder: widget.lastMessageBuilder, + faceUrl: conversationItem.faceUrl ?? "", + nickName: conversationItem.showName ?? "", + isDisturb: conversationItem.recvOpt != 0, + lastMsg: conversationItem.lastMessage, + isPined: isPined, + groupAtInfoList: + conversationItem.groupAtInfoList ?? [], + unreadCount: conversationItem.unreadCount ?? 0, + draftText: conversationItem.draftText, + onlineStatus: (widget.isShowOnlineStatus && + conversationItem.userID != null && + conversationItem.userID!.isNotEmpty) + ? onlineStatus + : null, + draftTimestamp: conversationItem.draftTimestamp, + convType: conversationItem.type), + onTap: () => onTapConvItem(conversationItem), + ), + ); + } + + return TUIKitScreenUtils.getDeviceWidget( + desktopWidget: AutoScrollTag( + key: ValueKey(conversationItem.conversationID), + controller: _autoScrollController, + index: index, + child: GestureDetector( + onSecondaryTapDown: (details) { + TUIKitWidePopup.showPopupWindow( + operationKey: TUIKitWideModalOperationKey + .conversationSecondaryMenu, + isDarkBackground: false, + borderRadius: const BorderRadius.all( + Radius.circular(4)), + context: context, + offset: Offset( + min( + details.globalPosition.dx, + MediaQuery.of(context).size.width - + 80), + min( + details.globalPosition.dy, + MediaQuery.of(context).size.height - + 130)), + child: (onClose) => _getSecondaryMenu( + conversationItem, onClose)); + }, + child: conversationLineItem(), + ), + ), + defaultWidget: AutoScrollTag( + key: ValueKey(conversationItem.conversationID), + controller: _autoScrollController, + index: index, + child: Slidable( + groupTag: 'conversation-list', + child: conversationLineItem(), + endActionPane: ActionPane( + extentRatio: + slideChildren.length > 2 ? 0.77 : 0.5, + motion: const DrawerMotion(), + children: slideChildren)), + )); + }) + : (widget.emptyBuilder != null + ? widget.emptyBuilder!() + : Container()); + } + + return TUIKitScreenUtils.getDeviceWidget( + defaultWidget: SlidableAutoCloseBehavior( + child: EasyRefresh( + header: CustomizeBallPulseHeader(color: theme.primaryColor), + onRefresh: () async { + model.refresh(); + }, + child: conversationList(), + ), + ), + desktopWidget: Scrollbar( + controller: _autoScrollController, + child: conversationList())); + }); + } +} diff --git a/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart new file mode 100644 index 0000000..ff1dc94 --- /dev/null +++ b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; + +import 'package:tencent_im_base/tencent_im_base.dart'; + +class TIMUIKitDraftText extends TIMUIKitStatelessWidget { + final BuildContext context; + final String draftText; + final double fontSize; + + TIMUIKitDraftText({ + Key? key, + this.fontSize = 14.0, + required this.context, + required this.draftText, + }) : super(key: key); + + String _getDraftShowText() { + final draftShowText = TIM_t("草稿"); + + return '[$draftShowText] '; + } + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + final TUITheme theme = value.theme; + return Row(children: [ + Text(_getDraftShowText(), + style: TextStyle( + color: theme.conversationItemDraftTextColor, + )), + Expanded( + child: Text( + draftText, + softWrap: true, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + height: 1.5, + color: theme.conversationItemLastMessageTextColor, + fontSize: fontSize), + )), + ]); + } +} diff --git a/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_item.dart b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_item.dart new file mode 100644 index 0000000..fa1676a --- /dev/null +++ b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_item.dart @@ -0,0 +1,206 @@ +// ignore_for_file: empty_catches + +import 'package:flutter/material.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart'; +import 'package:tencent_im_base/tencent_im_base.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; + +import 'package:tencent_cloud_chat_uikit/ui/utils/time_ago.dart'; + +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart'; +import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart'; +import 'package:tencent_cloud_chat_uikit/ui/widgets/unread_message.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; + + +class TIMConversationItem extends TIMUIKitStatelessWidget { + final String faceUrl; + final String nickName; + final V2TimMessage? lastMsg; + final int unreadCount; + final bool isPined; + final List groupAtInfoList; + final String? draftText; + final int? draftTimestamp; + final bool isDisturb; + final LastMessageBuilder? lastMessageBuilder; + final V2TimUserStatus? onlineStatus; + final int? convType; + final bool isCurrent; + + /// Control if shows the identifier that the conversation has a draft text, inputted in previous. + /// Also, you'd better specifying the `draftText` field for `TIMUIKitChat`, from the `draftText` in `V2TimConversation`, + /// to meet the identifier shows here. + final bool isShowDraft; + + TIMConversationItem({ + Key? key, + required this.isShowDraft, + required this.faceUrl, + required this.nickName, + required this.lastMsg, + this.onlineStatus, + required this.isPined, + this.isCurrent = false, + required this.unreadCount, + required this.groupAtInfoList, + required this.isDisturb, + this.draftText, + this.draftTimestamp, + this.lastMessageBuilder, + this.convType, + }) : super(key: key); + + Widget _getShowMsgWidget(BuildContext context) { + final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; + if (isShowDraft && draftText != null && draftText != "") { + return TIMUIKitDraftText( + context: context, + draftText: draftText ?? "", + fontSize: isDesktopScreen ? 12 : 14, + ); + } else if (lastMsg != null) { + if (lastMessageBuilder != null && + lastMessageBuilder!(lastMsg, groupAtInfoList) != null) { + return lastMessageBuilder!(lastMsg, groupAtInfoList)!; + } + return TIMUIKitLastMsg( + fontSize: isDesktopScreen ? 12 : 14, + groupAtInfoList: groupAtInfoList, + lastMsg: lastMsg, + context: context, + ); + } + + return Container( + height: 0, + ); + } + + bool isHaveSecondLine() { + return (isShowDraft && draftText != null && draftText != "") || + (lastMsg != null); + } + + Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) { + try { + if (draftTimestamp != null && draftTimestamp != 0) { + return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int), + style: TextStyle( + fontSize: 12, + color: theme.conversationItemTitmeTextColor, + )); + } else if (lastMsg != null) { + return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int), + style: TextStyle( + fontSize: 11, + color: theme.conversationItemTitmeTextColor, + )); + } + } catch (err) {} + + return Container(); + } + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + final TUITheme theme = value.theme; + final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; + return Container( + padding: const EdgeInsets.only(top: 6, bottom: 6, left: 16, right: 16), + decoration: BoxDecoration( + color: Colors.transparent, + + + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.only(top: 0, bottom: 2, right: 0), + child: SizedBox( + width: isDesktopScreen ? 40 : 44, + height: isDesktopScreen ? 40 : 44, + child: Stack( + fit: StackFit.expand, + clipBehavior: Clip.none, + children: [ + ClipOval( + child: Avatar( + onlineStatus: onlineStatus, + faceUrl: faceUrl, + showName: nickName, + type: convType), + ), + if (unreadCount != 0) + Positioned( + top: isDisturb ? -2.5 : -4.5, + right: isDisturb ? -2.5 : -4.5, + child: UnconstrainedBox( + child: UnreadMessage( + width: isDisturb ? 10 : 18, + height: isDisturb ? 10 : 18, + unreadCount: isDisturb ? 0 : unreadCount), + ), + ) + ], + ), + ), + ), + Expanded( + child: Container( + height: 60, + margin: EdgeInsets.only(left: isDesktopScreen ? 10 : 12), + padding: const EdgeInsets.only(top: 0, bottom: 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + nickName, + softWrap: true, + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + height: 1, + color: Color(0xFFF7FAFA), + fontSize: isDesktopScreen ? 14 : 18, + fontWeight: FontWeight.w400, + ), + )), + _getTimeStringForChatWidget(context, theme), + ], + ), + if (isHaveSecondLine()) + const SizedBox( + height: 6, + ), + Row( + children: [ + Expanded(child: _getShowMsgWidget(context)), + if (isDisturb) + SizedBox( + width: 18, + height: 18, + child: Icon( + Icons.notifications_off, + color: theme.conversationItemNoNotificationIconColor, + size: isDesktopScreen ? 14 : 16.0, + ), + ) + ], + ), + ], + ), + )) + ], + ), + ); + } +} diff --git a/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart new file mode 100644 index 0000000..09a335a --- /dev/null +++ b/circle_app/lib/app/msg/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart @@ -0,0 +1,152 @@ +// ignore_for_file: unrelated_type_equality_checks + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; + +import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart'; + +import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; +import 'package:tencent_im_base/tencent_im_base.dart'; + +class TIMUIKitLastMsg extends StatefulWidget { + final V2TimMessage? lastMsg; + final List groupAtInfoList; + final BuildContext context; + final double fontSize; + + const TIMUIKitLastMsg( + {Key? key, + this.lastMsg, + required this.groupAtInfoList, + required this.context, + this.fontSize = 14.0}) + : super(key: key); + + @override + State createState() => _TIMUIKitLastMsgState(); +} + +class _TIMUIKitLastMsgState extends TIMUIKitState { + String groupTipsAbstractText = ""; + + @override + void initState() { + super.initState(); + _getMsgElem(); + } + + @override + void didUpdateWidget(covariant TIMUIKitLastMsg oldWidget) { + super.didUpdateWidget(oldWidget); + if ((oldWidget.lastMsg?.msgID != widget.lastMsg?.msgID) || + (oldWidget.lastMsg?.id != widget.lastMsg?.id)) { + _getMsgElem(); + } + } + + void _getMsgElem() async { + final isRevokedMessage = widget.lastMsg!.status == 6; + if (isRevokedMessage) { + final isSelf = widget.lastMsg!.isSelf ?? true; + final option1 = isSelf + ? TIM_t("您") + : widget.lastMsg!.nickName ?? widget.lastMsg?.sender; + if (mounted) { + setState(() { + groupTipsAbstractText = TIM_t_para( + "{{option1}}撤回了一条消息", "$option1撤回了一条消息")(option1: option1); + }); + } + } else { + final newText = await _getLastMsgShowText(widget.lastMsg, widget.context); + if (mounted) { + setState(() { + groupTipsAbstractText = newText; + }); + } + } + } + + Future _getLastMsgShowText( + V2TimMessage? message, BuildContext context) async { + final msgType = message!.elemType; + switch (msgType) { + case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM: + return TIM_t("[自定义]"); + case MessageElemType.V2TIM_ELEM_TYPE_SOUND: + return TIM_t("[语音]"); + case MessageElemType.V2TIM_ELEM_TYPE_TEXT: + return (widget.lastMsg?.textElem?.text)?.trim() ?? ""; + case MessageElemType.V2TIM_ELEM_TYPE_FACE: + return TIM_t("[表情]"); + case MessageElemType.V2TIM_ELEM_TYPE_FILE: + final option1 = widget.lastMsg!.fileElem!.fileName; + return TIM_t_para("[文件] {{option1}}", "[文件] $option1")( + option1: option1); + case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS: + return await MessageUtils.groupTipsMessageAbstract( + widget.lastMsg!.groupTipsElem!, []); + case MessageElemType.V2TIM_ELEM_TYPE_IMAGE: + return TIM_t("[图片]"); + case MessageElemType.V2TIM_ELEM_TYPE_VIDEO: + return TIM_t("[视频]"); + case MessageElemType.V2TIM_ELEM_TYPE_LOCATION: + return TIM_t("[位置]"); + case MessageElemType.V2TIM_ELEM_TYPE_MERGER: + return TIM_t("[聊天记录]"); + default: + return TIM_t("未知消息"); + } + } + + Icon? _getIconByMsgStatus(BuildContext context) { + final msgStatus = widget.lastMsg!.status; + final theme = Provider.of(context).theme; + if (msgStatus == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) { + return Icon(Icons.error, color: theme.cautionColor, size: 16); + } + if (msgStatus == MessageStatus.V2TIM_MSG_STATUS_SENDING) { + return Icon(Icons.arrow_back, color: theme.weakTextColor, size: 16); + } + return null; + } + + String _getAtMessage() { + String msg = ""; + for (var item in widget.groupAtInfoList) { + if (item!.atType == 1) { + msg = TIM_t("[有人@我] "); + } else { + msg = TIM_t("[@所有人] "); + } + } + return msg; + } + + @override + Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + final TUITheme theme = value.theme; + final icon = _getIconByMsgStatus(context); + return Row(children: [ + if (icon != null) + Container( + margin: const EdgeInsets.only(right: 2), + child: icon, + ), + if (widget.groupAtInfoList.isNotEmpty) + Text(_getAtMessage(), + style: TextStyle( + color: theme.cautionColor, fontSize: widget.fontSize)), + Expanded( + child: Text( + groupTipsAbstractText, + softWrap: true, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + height: 1, color: theme.weakTextColor, fontSize: widget.fontSize), + )), + ]); + } +} diff --git a/circle_app/lib/app/msg/view.dart b/circle_app/lib/app/msg/view.dart index a07366a..04e1320 100644 --- a/circle_app/lib/app/msg/view.dart +++ b/circle_app/lib/app/msg/view.dart @@ -5,6 +5,8 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; + +import 'TIMUIKitConversation/tim_uikit_conversation.dart'; import 'logic.dart'; class MsgPage extends StatelessWidget { @@ -210,10 +212,14 @@ class MsgPage extends StatelessWidget { } msgWdiget(context) { - return TIMUIKitConversation( + return TIMConversation( onTapItem: (selectedConv) { Get.toNamed(AppRoutes.Chat); - } + }, + ); } + Widget msgItem(conv,status){ + return Container(); + } } diff --git a/circle_app/lib/app/splash/logic.dart b/circle_app/lib/app/splash/logic.dart index e8fa016..8203839 100644 --- a/circle_app/lib/app/splash/logic.dart +++ b/circle_app/lib/app/splash/logic.dart @@ -15,7 +15,7 @@ class SplashLogic extends GetxController { @override void onInit() async{ super.onInit(); - if(getAuthorization()==''){ + if((await getAuthorization()).isEmpty){ Get.toNamed(AppRoutes.Login); }else{ var data = diff --git a/circle_app/lib/network/dio_manager.dart b/circle_app/lib/network/dio_manager.dart index 60010cb..3f3c08f 100644 --- a/circle_app/lib/network/dio_manager.dart +++ b/circle_app/lib/network/dio_manager.dart @@ -171,7 +171,6 @@ class DioManager { // json转model String jsonStr = json.encode(response.data); Map responseMap = json.decode(jsonStr); - return responseMap; } on DioException catch (e) { // // DioError是指返回值不为200的情况 diff --git a/circle_app/macos/Flutter/GeneratedPluginRegistrant.swift b/circle_app/macos/Flutter/GeneratedPluginRegistrant.swift index 79ce50f..6be27df 100644 --- a/circle_app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/circle_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import desktop_drop import device_info_plus_macos import fc_native_video_thumbnail_for_us import file_selector_macos +import geolocator_apple import package_info_plus_macos import pasteboard import path_provider_foundation @@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FcNativeVideoThumbnailPlugin.register(with: registry.registrar(forPlugin: "FcNativeVideoThumbnailPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))