This commit is contained in:
abu 2025-09-09 10:57:52 +08:00
parent c4c59d3ec3
commit d61b852883
21 changed files with 1797 additions and 1008 deletions

BIN
assets/images/group.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -6,9 +6,9 @@ import 'package:loopin/utils/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
class ChatDetailController extends GetxController { class ChatDetailController extends GetxController {
final String userID; final String id;
ChatDetailController({required this.userID}); ChatDetailController({required this.id});
final ScrollController chatController = ScrollController(); final ScrollController chatController = ScrollController();
final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs; final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs;

View File

@ -24,6 +24,7 @@ class ImUserInfoController extends GetxController {
"area": "", "area": "",
"areaCode": "", "areaCode": "",
"openId": "", "openId": "",
"tag": "",
}.obs; }.obs;
final role = 0.obs; final role = 0.obs;
final level = 0.obs; final level = 0.obs;
@ -44,6 +45,7 @@ class ImUserInfoController extends GetxController {
"area": "", "area": "",
"areaCode": "", "areaCode": "",
"openId": "", "openId": "",
"tag": "",
}); });
role.value = userInfo.role ?? 0; role.value = userInfo.role ?? 0;

View File

@ -71,24 +71,26 @@ class ImMessageListenerService extends GetxService {
/// ///
void _handleNewMessage(V2TimMessage message) async { void _handleNewMessage(V2TimMessage message) async {
final userID = message.sender ?? ''; final id = message.sender ?? message.groupID ?? '';
if (userID.isEmpty) return; final isGroup = message.groupID != null && message.groupID!.isNotEmpty;
final conversationID = isGroup ? 'group_${message.groupID}' : 'c2c_${message.userID}';
if (id.isEmpty) return;
/// ///
if ((Get.currentRoute == '/chat' || Get.currentRoute == '/chatNoFriend' || Get.currentRoute == '/chatGroup') && Get.isRegistered<ChatDetailController>()) { if ((Get.currentRoute == '/chat' || Get.currentRoute == '/chatNoFriend' || Get.currentRoute == '/chatGroup') && Get.isRegistered<ChatDetailController>()) {
final chatDetailController = Get.find<ChatDetailController>(); final chatDetailController = Get.find<ChatDetailController>();
// // (chatDetailController.id是通过chat_binding传入的userID,groupID)
if (chatDetailController.userID == userID) { if (chatDetailController.id == id) {
// //
insertTimeLabel(message); insertTimeLabel(message);
// //
await ImService.instance.clearConversationUnreadCount(conversationID: 'c2c_$userID'); await ImService.instance.clearConversationUnreadCount(conversationID: conversationID);
} }
return; return;
} }
// 线 // 线
if (!LifecycleHandler.isInForeground) { if (!LifecycleHandler.isInForeground) {
logger.i("App 当前在后台,收到来自 $userID 的消息 ${message.msgID},暂不展示弹窗"); logger.i("App 当前在后台,收到来自 $id 的消息 ${message.msgID},暂不展示弹窗");
// return; // return;
} }
//TODO //TODO

View File

@ -17,6 +17,9 @@ import 'package:tencent_cloud_chat_sdk/enum/friend_application_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_response_type_enum.dart'; import 'package:tencent_cloud_chat_sdk/enum/friend_response_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart'; import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_add_opt_enum.dart'; import 'package:tencent_cloud_chat_sdk/enum/group_add_opt_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_application_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_member_filter_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_member_role_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart'; import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart'; import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
@ -30,7 +33,17 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.da
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_operation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_search_param.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart';
@ -40,6 +53,8 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_conversation_manager.dart'; import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_conversation_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_friendship_manager.dart'; import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_friendship_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_group_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_message_manager.dart'; import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_message_manager.dart';
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart'; import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
@ -734,6 +749,7 @@ class ImService {
return ImResult.wrap(res); return ImResult.wrap(res);
} }
///------------------------------------
/// ///
Future<ImResult<String>> createGroup({ Future<ImResult<String>> createGroup({
String? groupID, String? groupID,
@ -768,4 +784,299 @@ class ImService {
return ImResult.wrap(res); return ImResult.wrap(res);
} }
///
///
/// - (AVChatRoom) API
/// - SDK 1 10 7008
Future<ImResult<List<V2TimGroupInfo>>> getJoinedGroupList() async {
final res = await V2TIMGroupManager().getJoinedGroupList();
return ImResult.wrap(res);
}
/// work群work群只能邀请进群
Future<ImResult<void>> joinGroup({
required String groupID,
required String message,
}) async {
final res = await TIMManager.instance.joinGroup(
groupID: groupID,
message: message,
);
return ImResult.wrapNoData(res);
}
///
///
/// - [groupIDList] ID
Future<ImResult<List<V2TimGroupInfoResult>>> getGroupsInfo({
required List<String> groupIDList,
}) async {
final res = await V2TIMGroupManager().getGroupsInfo(groupIDList: groupIDList);
return ImResult.wrap(res);
}
///
///
///
/// - [info]
Future<ImResult<void>> setGroupInfo({
required V2TimGroupInfo info,
}) async {
final res = await V2TIMGroupManager().setGroupInfo(info: info);
return ImResult.wrapNoData(res);
}
///
///
///
/// [groupID] ID
/// [filter]
/// - V2TIM_GROUP_MEMBER_FILTER_ALL
/// - V2TIM_GROUP_MEMBER_FILTER_OWNER
/// - V2TIM_GROUP_MEMBER_FILTER_ADMIN
/// - V2TIM_GROUP_MEMBER_FILTER_COMMON
/// [nextSeq] 0
/// [count] 100
/// [offset] Web
Future<ImResult<V2TimGroupMemberInfoResult>> getGroupMemberList({
required String groupID,
required GroupMemberFilterTypeEnum filter,
required String nextSeq,
int count = 20,
int offset = 0,
}) async {
final res = await V2TIMGroupManager().getGroupMemberList(
groupID: groupID,
filter: filter,
nextSeq: nextSeq,
count: count,
offset: offset,
);
return ImResult.wrap(res);
}
///
///
/// [groupID] ID
/// [memberList] ID
Future<ImResult<List<V2TimGroupMemberFullInfo>>> getGroupMembersInfo({
required String groupID,
required List<String> memberList,
}) async {
final res = await V2TIMGroupManager().getGroupMembersInfo(
groupID: groupID,
memberList: memberList,
);
return ImResult.wrap(res);
}
///
///
/// [groupID] ID
/// [userID] ID
/// [nameCard]
/// [customInfo]
Future<ImResult<void>> setGroupMemberInfo({
required String groupID,
required String userID,
String? nameCard,
Map<String, String>? customInfo,
}) async {
final res = await V2TIMGroupManager().setGroupMemberInfo(
groupID: groupID,
userID: userID,
nameCard: nameCard,
customInfo: customInfo,
);
return ImResult.wrapNoData(res);
}
/// (work群的入群方式)
///
/// [groupID] ID
/// [userList] userID
///
///
///
/// ```
/// Work
/// MeetingPublic REST API 使 App
/// AVChatRoom
/// ```
Future<ImResult<List<V2TimGroupMemberOperationResult>>> inviteUserToGroup({
required String groupID,
required List<String> userList,
}) async {
final res = await TIMGroupManager.instance.inviteUserToGroup(
groupID: groupID,
userList: userList,
);
return ImResult.wrap(res);
}
///
///
/// [groupID] ID
/// [memberList] userID
/// [duration]
/// [reason]
///
///
/// ```
/// Work APP
/// PublicMeeting APP
/// AVChatRoommuteGroupMember
/// ```
Future<ImResult<void>> kickGroupMember({
required String groupID,
required List<String> memberList,
int? duration,
String? reason,
}) async {
final res = await TIMGroupManager.instance.kickGroupMember(
groupID: groupID,
memberList: memberList,
duration: duration,
reason: reason,
);
return ImResult.wrapNoData(res);
}
///
///
/// [groupID] ID
/// [userID] userID
/// [role]
///
///
/// ```
/// PublicMeeting
///
/// transferGroupOwner
/// ```
Future<ImResult<void>> setGroupMemberRole({
required String groupID,
required String userID,
required GroupMemberRoleTypeEnum role,
}) async {
final res = await TIMGroupManager.instance.setGroupMemberRole(
groupID: groupID,
userID: userID,
role: role,
);
return ImResult.wrapNoData(res);
}
///
///
/// [groupID] ID
/// [userID] userID
///
///
/// ```
/// WorkPublicMeeting
/// AVChatRoom
/// ```
Future<ImResult<void>> transferGroupOwner({
required String groupID,
required String userID,
}) async {
final res = await TIMGroupManager.instance.transferGroupOwner(
groupID: groupID,
userID: userID,
);
return ImResult.wrapNoData(res);
}
///
Future<ImResult<V2TimGroupApplicationResult>> getGroupApplicationList() async {
final res = await TIMGroupManager.instance.getGroupApplicationList();
return ImResult.wrap(res);
}
///
Future<ImResult<void>> acceptGroupApplication({
required String groupID,
String? reason,
required String fromUser,
required String toUser, //
int? addTime,
GroupApplicationTypeEnum? type,
V2TimGroupApplication? application,
String? webMessageInstance,
}) async {
final res = await TIMGroupManager.instance.acceptGroupApplication(
groupID: groupID,
reason: reason,
fromUser: fromUser,
toUser: toUser,
addTime: addTime,
type: type,
application: application,
);
return ImResult.wrapNoData(res);
}
///
Future<ImResult> refuseGroupApplication({
required String groupID,
String? reason,
required String fromUser,
required String toUser,
required int addTime,
required GroupApplicationTypeEnum type,
V2TimGroupApplication? application,
}) async {
final res = await TIMGroupManager.instance.refuseGroupApplication(
groupID: groupID,
fromUser: fromUser,
toUser: toUser,
addTime: addTime,
type: type,
application: application,
);
return ImResult.wrapNoData(res);
}
///
Future<ImResult> setGroupApplicationRead() async {
final res = await TIMGroupManager.instance.setGroupApplicationRead();
return ImResult.wrapNoData(res);
}
///
Future<ImResult<List<V2TimGroupInfo>>> searchGroups({
required V2TimGroupSearchParam searchParam,
}) async {
final res = await TIMGroupManager.instance.searchGroups(searchParam: searchParam);
return ImResult.wrap(res);
}
///
Future<ImResult<V2GroupMemberInfoSearchResult>> searchGroupMembers({
required V2TimGroupMemberSearchParam param,
}) async {
final res = await TIMGroupManager.instance.searchGroupMembers(param: param);
return ImResult.wrap(res);
}
/// (Work,)
Future<ImResult<void>> dismissGroup({
required String groupID,
}) async {
final res = await TIMManager.instance.dismissGroup(groupID: groupID);
return ImResult.wrapNoData(res);
}
/// 退 ,PublicMeetingAVChatRoom退 dismissGroup
Future<ImResult<void>> quitGroup({
required String groupID,
}) async {
final res = await TIMManager.instance.quitGroup(groupID: groupID);
return ImResult.wrapNoData(res);
}
} }

View File

@ -6,6 +6,7 @@ class ChatBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
V2TimConversation conversation = Get.arguments; V2TimConversation conversation = Get.arguments;
Get.put(ChatDetailController(userID: conversation.userID!)); final id = conversation.userID ?? conversation.groupID;
Get.put(ChatDetailController(id: id!));
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/im_friend_listeners.dart';
import 'package:loopin/api/shop_api.dart'; import 'package:loopin/api/shop_api.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
@ -117,7 +118,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
}); });
final data = res['data']['records']; final data = res['data']['records'];
print('商品返回数据------------------------->${data}'); logger.w('商品返回数据:$index------------------------->$data');
tab.dataList.addAll(data); tab.dataList.addAll(data);
// logger.w(res); // logger.w(res);
@ -132,7 +133,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
'type': 1, 'type': 1,
}); });
final data = res['data']; final data = res['data'];
// logger.w(res); logger.w(res);
swiperData.assignAll(data); swiperData.assignAll(data);
} }

View File

@ -2,23 +2,28 @@
library; library;
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:loopin/IM/controller/chat_detail_controller.dart'; import 'package:loopin/IM/controller/chat_detail_controller.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_result.dart'; import 'package:loopin/IM/im_result.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/image_viewer.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/components/preview_video.dart'; import 'package:loopin/components/preview_video.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:loopin/utils/audio_player_service.dart'; import 'package:loopin/utils/audio_player_service.dart';
import 'package:loopin/utils/snapshot.dart'; import 'package:loopin/utils/snapshot.dart';
import 'package:loopin/utils/voice_service.dart'; import 'package:loopin/utils/voice_service.dart';
import 'package:mime/mime.dart';
import 'package:shirne_dialog/shirne_dialog.dart'; import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:video_player/video_player.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import '../../styles/index.dart'; import '../../styles/index.dart';
@ -36,6 +41,8 @@ class Chat extends StatefulWidget {
} }
class _ChatState extends State<Chat> with SingleTickerProviderStateMixin { class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
final ImagePicker _picker = ImagePicker();
late final ChatDetailController controller; late final ChatDetailController controller;
// //
late final Rx<V2TimConversation> arguments; late final Rx<V2TimConversation> arguments;
@ -346,6 +353,13 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
data: item, data: item,
child: Ink( child: Ink(
child: InkWell( child: InkWell(
onTap: () {
//
Get.to(() => ImageViewer(
images: [imagePaths.first],
index: 0,
));
},
overlayColor: WidgetStateProperty.all(Colors.transparent), overlayColor: WidgetStateProperty.all(Colors.transparent),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
@ -525,9 +539,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
} }
// //
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) { else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
//price,title,url,sell // final makeJson = jsonEncode({
// "price": shopObj['price'],
// "title": shopObj['name'],
// "url": shopObj['pic'],
// "sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
// "goodsId": shopObj['id'],
// "userID": Get.find<ImUserInfoController>().userID.value,
// });
final obj = jsonDecode(item.customElem!.data!); final obj = jsonDecode(item.customElem!.data!);
logger.e(obj); logger.e(obj);
final goodsId = obj['goodsId'];
final userID = obj['userID'];
final url = obj['url']; final url = obj['url'];
final title = obj['title']; final title = obj['title'];
final price = obj['price']; final price = obj['price'];
@ -535,6 +558,10 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
msgtpl.add(RenderChatItem( msgtpl.add(RenderChatItem(
data: item, data: item,
child: GestureDetector( child: GestureDetector(
onTap: () {
// ID
Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
},
child: Container( child: Container(
width: 160, width: 160,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
@ -596,11 +623,6 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
], ],
), ),
), ),
onTap: () {
// ID
// Get.toNamed('/goods');
Get.toNamed('/goods', arguments: {});
},
), ),
)); ));
} }
@ -609,10 +631,12 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
/// {imgUrl,videoUrl,width,height} /// {imgUrl,videoUrl,width,height}
final obj = jsonDecode(item.customElem!.data!); final obj = jsonDecode(item.customElem!.data!);
logger.e(obj); logger.e(obj);
final videoId = obj['videoId'];
final videoUrl = obj['videoUrl']; final videoUrl = obj['videoUrl'];
final imgUrl = obj['imgUrl']; final imgUrl = obj['imgUrl'];
final width = obj['width'] as num; final width = obj['width'] as num;
final height = obj['height'] as num; final height = obj['height'] as num;
final isHorizontal = width > height;
msgtpl.add(RenderChatItem( msgtpl.add(RenderChatItem(
data: item, data: item,
child: Ink( child: Ink(
@ -625,9 +649,15 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
child: Container(
width: 120,
height: 240,
color: Colors.black,
child: NetworkOrAssetImage( child: NetworkOrAssetImage(
imageUrl: imgUrl, imageUrl: imgUrl,
width: 120, fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
placeholderAsset: 'assets/images/bk.jpg',
),
), ),
), ),
const Align( const Align(
@ -642,31 +672,32 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
), ),
), ),
onTap: () { onTap: () {
showGeneralDialog( Get.toNamed('/videoDetail', arguments: {'videoId': videoId});
context: context, // showGeneralDialog(
barrierColor: Colors.black.withAlpha((1.0 * 255).round()), // context: context,
pageBuilder: (_, __, ___) { // barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
return SafeArea( // pageBuilder: (_, __, ___) {
bottom: true, // return SafeArea(
child: Padding( // bottom: true,
padding: const EdgeInsets.only(bottom: 4), // child: Padding(
child: PreviewVideo( // padding: const EdgeInsets.only(bottom: 4),
videoUrl: videoUrl, // child: PreviewVideo(
width: width.toDouble(), // videoUrl: videoUrl,
height: height.toDouble(), // width: width.toDouble(),
), // height: height.toDouble(),
), // ),
); // ),
}, // );
transitionBuilder: (_, anim, __, child) { // },
return FadeTransition(opacity: anim, child: child); // transitionBuilder: (_, anim, __, child) {
}, // return FadeTransition(opacity: anim, child: child);
transitionDuration: const Duration(milliseconds: 200), // },
); // transitionDuration: const Duration(milliseconds: 200),
}, // );
onLongPress: () {
contextMenuDialog();
}, },
// onLongPress: () {
// contextMenuDialog();
// },
), ),
), ),
)); ));
@ -1022,7 +1053,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
} }
// //
void sendMessage(message) async { Future<void> sendMessage(message) async {
// //
List<V2TimMessage> messagesToInsert = []; List<V2TimMessage> messagesToInsert = [];
V2TimMessage? lastRealMsg; V2TimMessage? lastRealMsg;
@ -1192,6 +1223,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// =5 // =5
void sendVideo(videoFilePath, type, duration, snapshotPath) async { void sendVideo(videoFilePath, type, duration, snapshotPath) async {
final instance = MyDialog.loading('处理中');
final resImg = await IMMessage().createVideoMessage( final resImg = await IMMessage().createVideoMessage(
videoFilePath: videoFilePath, videoFilePath: videoFilePath,
type: type, type: type,
@ -1199,13 +1232,116 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
snapshotPath: snapshotPath, snapshotPath: snapshotPath,
); );
if (resImg.success) { if (resImg.success) {
sendMessage(resImg.data?.messageInfo); await sendMessage(resImg.data?.messageInfo);
instance.close();
} }
} }
//
Future<void> showPicker(BuildContext context) async {
showModalBottomSheet(
context: context,
backgroundColor: Colors.white, //
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () async {
Navigator.pop(context);
await _pickPhoto();
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.photo),
SizedBox(width: 8),
Text("拍摄照片"),
],
),
),
),
InkWell(
onTap: () async {
Navigator.pop(context);
await _pickVideo();
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.videocam),
SizedBox(width: 8),
Text("拍摄视频"),
],
),
),
),
const Divider(height: 1),
InkWell(
onTap: () => Navigator.pop(context),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
// Icon(Icons.close),
// SizedBox(width: 8),
Text("取消"),
],
),
),
),
],
),
);
},
);
}
///
Future<void> _pickPhoto() async {
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: 1920,
maxHeight: 1080,
imageQuality: 90,
);
if (photo != null) {
logger.w("照片路径: ${photo.path}");
sendImage(photo.path);
}
}
///
Future<void> _pickVideo() async {
final XFile? video = await _picker.pickVideo(
source: ImageSource.camera,
maxDuration: const Duration(minutes: 1),
);
if (video == null) return;
logger.w("视频路径: ${video.path}");
//
final snapshot = await generateVideoThumbnail(video.path) ?? '';
//
final controller = VideoPlayerController.file(File(video.path));
await controller.initialize();
final duration = controller.value.duration.inSeconds;
await controller.dispose();
// mimeType
final mimeType = lookupMimeType(video.path) ?? 'video/mp4';
sendVideo(video.path, mimeType, duration, snapshot);
}
// //
void handleChooseAction(key) { void handleChooseAction(key) {
MyDialog.toast('$key'); // MyDialog.toast('$key');
switch (key) { switch (key) {
case 'photo': case 'photo':
// .... // ....
@ -1213,6 +1349,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
break; break;
case 'camera': case 'camera':
// .... // ....
showPicker(context);
break; break;
case 'redpacket': case 'redpacket':
sendRedPacketDialog(); sendRedPacketDialog();
@ -1421,35 +1558,35 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// //
void contextMenuDialog() { void contextMenuDialog() {
showDialog( // showDialog(
context: context, // context: context,
builder: (context) { // builder: (context) {
return SimpleDialog( // return SimpleDialog(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
surfaceTintColor: Colors.white, // surfaceTintColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(vertical: 5.0), // contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
children: [ // children: [
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
onPressed: () {}, // onPressed: () {},
), // ),
], // ],
); // );
}, // },
); // );
} }
// //

View File

@ -1,31 +1,34 @@
/// ///
library; library;
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:loopin/IM/controller/chat_detail_controller.dart'; import 'package:loopin/IM/controller/chat_detail_controller.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_result.dart'; import 'package:loopin/IM/im_result.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/image_viewer.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/components/preview_video.dart'; import 'package:loopin/components/preview_video.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:loopin/utils/audio_player_service.dart'; import 'package:loopin/utils/audio_player_service.dart';
import 'package:loopin/utils/snapshot.dart'; import 'package:loopin/utils/snapshot.dart';
import 'package:loopin/utils/voice_service.dart'; import 'package:loopin/utils/voice_service.dart';
import 'package:mime/mime.dart';
import 'package:shirne_dialog/shirne_dialog.dart'; import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:video_player/video_player.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import '../../styles/index.dart'; import '../../styles/index.dart';
import '../../utils/index.dart'; import '../../utils/index.dart';
import './components/redpacket.dart'; import './components/redpacket.dart';
import './components/richtext.dart'; import './components/richtext.dart';
// import 'mock/chat_json.dart';
import 'mock/emoj_json.dart'; import 'mock/emoj_json.dart';
class ChatGroup extends StatefulWidget { class ChatGroup extends StatefulWidget {
@ -36,9 +39,11 @@ class ChatGroup extends StatefulWidget {
} }
class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin { class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin {
final ImagePicker _picker = ImagePicker();
late final ChatDetailController controller; late final ChatDetailController controller;
// //
late V2TimConversation arguments; final V2TimConversation arguments = Get.arguments;
late String selfUserId; late String selfUserId;
@ -91,7 +96,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
@override @override
void initState() { void initState() {
super.initState(); super.initState();
arguments = Get.arguments;
controller = Get.find<ChatDetailController>(); controller = Get.find<ChatDetailController>();
chatController = controller.chatController; chatController = controller.chatController;
@ -146,56 +150,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
super.dispose(); super.dispose();
} }
//
void setRemark() async {
String remark = '';
await MyDialog.confirm(
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
TextField(
onChanged: (value) => remark = value,
maxLength: 16,
maxLengthEnforcement: MaxLengthEnforcement.enforced, //
decoration: InputDecoration(
hintText: '请输入备注',
filled: true,
fillColor: const Color(0xFFF5F5F5),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Color(0xFFBE4EFF), width: 1),
),
),
)
],
),
title: '设置备注',
buttonText: '确认',
cancelText: '取消',
onConfirm: () async {
// print('备注为:$remark');
final res = await ImService.instance.setFriendInfo(userID: arguments.userID!, friendRemark: remark);
if (res.success) {
setState(() {
arguments.showName = remark;
});
} else {
print(res.desc);
print(arguments.userID);
MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
}
return true;
},
);
}
void cleanUnRead() async { void cleanUnRead() async {
if ((arguments.unreadCount ?? 0) > 0) { if ((arguments.unreadCount ?? 0) > 0) {
final res = await ImService.instance.clearConversationUnreadCount(conversationID: arguments.conversationID); final res = await ImService.instance.clearConversationUnreadCount(conversationID: arguments.conversationID);
@ -219,7 +173,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
// //
V2TimMessage? lastRealMsg; V2TimMessage? lastRealMsg;
// for (var msg in controller.chatList.reversed) {
for (var msg in controller.chatList.reversed) { for (var msg in controller.chatList.reversed) {
if (msg.localCustomData != 'time_label') { if (msg.localCustomData != 'time_label') {
lastRealMsg = msg; lastRealMsg = msg;
@ -227,12 +180,10 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
} }
} }
final lastMsg = lastRealMsg ?? arguments.lastMessage; // final lastMsg = lastRealMsg ?? arguments.lastMessage; //
print(lastMsg?.toLogString()); logger.w(lastMsg?.toLogString());
// final lastMsg = controller.chatList.isNotEmpty ? controller.chatList.last : arguments.lastMessage;
final res = await ImService.instance.getHistoryMessageList( final res = await ImService.instance.getHistoryMessageList(
userID: arguments.userID, groupID: arguments.groupID,
lastMsg: lastMsg, lastMsg: lastMsg,
); );
@ -241,26 +192,21 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
if (newMessages.isEmpty) { if (newMessages.isEmpty) {
hasMore = false; hasMore = false;
// MyDialog.toast('没有更多了~', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} }
if (initFlag && lastMsg != null) { if (initFlag && lastMsg != null) {
newMessages.insert(0, lastMsg); newMessages.insert(0, lastMsg);
// controller.scrollToBottom();
} }
controller.updateChatListWithTimeLabels(newMessages); controller.updateChatListWithTimeLabels(newMessages);
if (initFlag) { if (initFlag) {
// //
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (chatController.hasClients) { if (chatController.hasClients) {
// controller.scrollToBottom();
// final bottomPadding = MediaQuery.of(context).padding.bottom; //
// chatController.jumpTo(chatController.position.maxScrollExtent); // 60
chatController.jumpTo(0); chatController.jumpTo(0);
} }
}); });
} }
print('聊天数据加载成功'); logger.w('群聊消息加载成功');
} else { } else {
MyDialog.toast("获取聊天记录失败:${res.desc}", icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); MyDialog.toast("获取聊天记录失败:${res.desc}", icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} }
@ -343,6 +289,13 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
data: item, data: item,
child: Ink( child: Ink(
child: InkWell( child: InkWell(
onTap: () {
//
Get.to(() => ImageViewer(
images: [imagePaths.first],
index: 0,
));
},
overlayColor: WidgetStateProperty.all(Colors.transparent), overlayColor: WidgetStateProperty.all(Colors.transparent),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
@ -525,6 +478,8 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
//price,title,url,sell //price,title,url,sell
final obj = jsonDecode(item.customElem!.data!); final obj = jsonDecode(item.customElem!.data!);
logger.e(obj); logger.e(obj);
final goodsId = obj['goodsId'];
final userID = obj['userID'];
final url = obj['url']; final url = obj['url'];
final title = obj['title']; final title = obj['title'];
final price = obj['price']; final price = obj['price'];
@ -532,6 +487,10 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
msgtpl.add(RenderChatItem( msgtpl.add(RenderChatItem(
data: item, data: item,
child: GestureDetector( child: GestureDetector(
onTap: () {
// ID
Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
},
child: Container( child: Container(
width: 160, width: 160,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
@ -593,11 +552,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
], ],
), ),
), ),
onTap: () {
// ID
// Get.toNamed('/goods');
Get.toNamed('/goods', arguments: {});
},
), ),
)); ));
} }
@ -606,10 +560,12 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
/// {imgUrl,videoUrl,width,height} /// {imgUrl,videoUrl,width,height}
final obj = jsonDecode(item.customElem!.data!); final obj = jsonDecode(item.customElem!.data!);
logger.e(obj); logger.e(obj);
final videoId = obj['videoId'];
final videoUrl = obj['videoUrl']; final videoUrl = obj['videoUrl'];
final imgUrl = obj['imgUrl']; final imgUrl = obj['imgUrl'];
final width = obj['width'] as num; final width = obj['width'] as num;
final height = obj['height'] as num; final height = obj['height'] as num;
final isHorizontal = width > height;
msgtpl.add(RenderChatItem( msgtpl.add(RenderChatItem(
data: item, data: item,
child: Ink( child: Ink(
@ -622,9 +578,15 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
child: Container(
width: 120,
height: 240,
color: Colors.black,
child: NetworkOrAssetImage( child: NetworkOrAssetImage(
imageUrl: imgUrl, imageUrl: imgUrl,
width: 120, fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
placeholderAsset: 'assets/images/bk.jpg',
),
), ),
), ),
const Align( const Align(
@ -639,31 +601,32 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
), ),
), ),
onTap: () { onTap: () {
showGeneralDialog( Get.toNamed('/videoDetail', arguments: {'videoId': videoId});
context: context, // showGeneralDialog(
barrierColor: Colors.black.withAlpha((1.0 * 255).round()), // context: context,
pageBuilder: (_, __, ___) { // barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
return SafeArea( // pageBuilder: (_, __, ___) {
bottom: true, // return SafeArea(
child: Padding( // bottom: true,
padding: const EdgeInsets.only(bottom: 4), // child: Padding(
child: PreviewVideo( // padding: const EdgeInsets.only(bottom: 4),
videoUrl: videoUrl, // child: PreviewVideo(
width: width.toDouble(), // videoUrl: videoUrl,
height: height.toDouble(), // width: width.toDouble(),
), // height: height.toDouble(),
), // ),
); // ),
}, // );
transitionBuilder: (_, anim, __, child) { // },
return FadeTransition(opacity: anim, child: child); // transitionBuilder: (_, anim, __, child) {
}, // return FadeTransition(opacity: anim, child: child);
transitionDuration: const Duration(milliseconds: 200), // },
); // transitionDuration: const Duration(milliseconds: 200),
}, // );
onLongPress: () {
contextMenuDialog();
}, },
// onLongPress: () {
// contextMenuDialog();
// },
), ),
), ),
)); ));
@ -1019,7 +982,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
} }
// //
void sendMessage(message) async { Future<void> sendMessage(message) async {
// //
List<V2TimMessage> messagesToInsert = []; List<V2TimMessage> messagesToInsert = [];
V2TimMessage? lastRealMsg; V2TimMessage? lastRealMsg;
@ -1054,7 +1017,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
late final ImResult res; late final ImResult res;
res = await IMMessage().sendMessage( res = await IMMessage().sendMessage(
msg: message, msg: message,
toUserID: arguments.userID, toUserID: arguments.groupID,
); );
if (res.success && res.data != null) { if (res.success && res.data != null) {
@ -1189,6 +1152,8 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
// =5 // =5
void sendVideo(videoFilePath, type, duration, snapshotPath) async { void sendVideo(videoFilePath, type, duration, snapshotPath) async {
final instance = MyDialog.loading('处理中');
final resImg = await IMMessage().createVideoMessage( final resImg = await IMMessage().createVideoMessage(
videoFilePath: videoFilePath, videoFilePath: videoFilePath,
type: type, type: type,
@ -1196,13 +1161,116 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
snapshotPath: snapshotPath, snapshotPath: snapshotPath,
); );
if (resImg.success) { if (resImg.success) {
sendMessage(resImg.data?.messageInfo); await sendMessage(resImg.data?.messageInfo);
instance.close();
} }
} }
//
Future<void> showPicker(BuildContext context) async {
showModalBottomSheet(
context: context,
backgroundColor: Colors.white, //
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () async {
Navigator.pop(context);
await _pickPhoto();
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.photo),
SizedBox(width: 8),
Text("拍摄照片"),
],
),
),
),
InkWell(
onTap: () async {
Navigator.pop(context);
await _pickVideo();
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.videocam),
SizedBox(width: 8),
Text("拍摄视频"),
],
),
),
),
const Divider(height: 1),
InkWell(
onTap: () => Navigator.pop(context),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
// Icon(Icons.close),
// SizedBox(width: 8),
Text("取消"),
],
),
),
),
],
),
);
},
);
}
///
Future<void> _pickPhoto() async {
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: 1920,
maxHeight: 1080,
imageQuality: 90,
);
if (photo != null) {
logger.w("照片路径: ${photo.path}");
sendImage(photo.path);
}
}
///
Future<void> _pickVideo() async {
final XFile? video = await _picker.pickVideo(
source: ImageSource.camera,
maxDuration: const Duration(minutes: 1),
);
if (video == null) return;
logger.w("视频路径: ${video.path}");
//
final snapshot = await generateVideoThumbnail(video.path) ?? '';
//
final controller = VideoPlayerController.file(File(video.path));
await controller.initialize();
final duration = controller.value.duration.inSeconds;
await controller.dispose();
// mimeType
final mimeType = lookupMimeType(video.path) ?? 'video/mp4';
sendVideo(video.path, mimeType, duration, snapshot);
}
// //
void handleChooseAction(key) { void handleChooseAction(key) {
MyDialog.toast('$key'); // MyDialog.toast('$key');
switch (key) { switch (key) {
case 'photo': case 'photo':
// .... // ....
@ -1210,6 +1278,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
break; break;
case 'camera': case 'camera':
// .... // ....
showPicker(context);
break; break;
case 'redpacket': case 'redpacket':
sendRedPacketDialog(); sendRedPacketDialog();
@ -1418,35 +1487,35 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
// //
void contextMenuDialog() { void contextMenuDialog() {
showDialog( // showDialog(
context: context, // context: context,
builder: (context) { // builder: (context) {
return SimpleDialog( // return SimpleDialog(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
surfaceTintColor: Colors.white, // surfaceTintColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(vertical: 5.0), // contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
children: [ // children: [
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
onPressed: () {}, // onPressed: () {},
), // ),
SimpleDialogOption( // SimpleDialogOption(
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')), // child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
onPressed: () {}, // onPressed: () {},
), // ),
], // ],
); // );
}, // },
); // );
} }
// //
@ -1496,13 +1565,13 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
}, },
), ),
titleSpacing: 1.0, titleSpacing: 1.0,
title: Obx(() { title: Text(
return Text(
// '${arguments['title']}',
'${arguments.showName}', '${arguments.showName}',
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'), style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
); maxLines: 1,
}), overflow: TextOverflow.ellipsis,
softWrap: false,
),
flexibleSpace: Container( flexibleSpace: Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -1603,7 +1672,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
switch (selected) { switch (selected) {
case 'remark': case 'remark':
print('点击了备注'); print('点击了备注');
setRemark();
break; break;
case 'not': case 'not':
print('点击了免打扰'); print('点击了免打扰');
@ -2189,13 +2257,13 @@ class RenderChatItem extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end, crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [ children: [
// Text( Text(
// displayName ?? '未知昵称', displayName ?? '未知昵称',
// style: const TextStyle(color: Colors.grey, fontSize: 12.0), style: const TextStyle(color: Colors.grey, fontSize: 12.0),
// ), ),
// const SizedBox( const SizedBox(
// height: 3.0, height: 3.0,
// ), ),
Stack( Stack(
children: [ children: [
// //

View File

@ -270,7 +270,12 @@ class ChatPageState extends State<ChatPage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Column( GestureDetector(
onTap: () {
//
Get.toNamed('/groupList');
},
child: Column(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
'assets/images/svg/order.svg', 'assets/images/svg/order.svg',
@ -280,6 +285,7 @@ class ChatPageState extends State<ChatPage> {
Text('群聊'), Text('群聊'),
], ],
), ),
),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
// //
@ -351,6 +357,7 @@ class ChatPageState extends State<ChatPage> {
return Ink( return Ink(
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], // // color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //
child: InkWell( child: InkWell(
key: ValueKey(chatList[index].conversation.conversationID),
splashColor: Colors.grey[200], splashColor: Colors.grey[200],
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),

View File

@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/flutter_html.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/shop_api.dart'; import 'package:loopin/api/shop_api.dart';
@ -33,7 +34,7 @@ class Goods extends StatefulWidget {
} }
class _GoodsState extends State<Goods> { class _GoodsState extends State<Goods> {
// late int shopId; //id final shareUserId = Get.arguments['userID'] ?? ''; //id,
dynamic shopObj; dynamic shopObj;
late ScrollController scrollController = ScrollController(); late ScrollController scrollController = ScrollController();
final ChatController chatController = Get.find<ChatController>(); final ChatController chatController = Get.find<ChatController>();
@ -124,6 +125,8 @@ class _GoodsState extends State<Goods> {
"title": shopObj['name'], "title": shopObj['name'],
"url": shopObj['pic'], "url": shopObj['pic'],
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')), "sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
"goodsId": shopObj['id'],
"userID": Get.find<ImUserInfoController>().userID.value,
}); });
final res = await IMMessage().createCustomMessage( final res = await IMMessage().createCustomMessage(
data: makeJson, data: makeJson,

View File

@ -0,0 +1,202 @@
///
library;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/behavior/custom_scroll_behavior.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
class Grouplist extends StatefulWidget {
const Grouplist({super.key});
@override
State<Grouplist> createState() => GrouplistState();
}
class GrouplistState extends State<Grouplist> with SingleTickerProviderStateMixin {
bool isLoading = false; //
bool hasMore = true; //
String page = '';
List<V2TimGroupInfo> dataList = <V2TimGroupInfo>[];
///-------------------
@override
void initState() {
super.initState();
getData();
}
//
Future<void> getData() async {
final res = await ImService.instance.getJoinedGroupList();
if (res.success && res.data != null) {
for (var item in res.data!) {
logger.i('获取成功:${item.toLogString()}');
}
final isFinished = true;
if (isFinished) {
setState(() {
hasMore = false;
});
}
setState(() {
dataList.addAll(res.data!);
});
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//
Future<void> handleRefresh() async {
dataList.clear();
page = '';
getData();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
'群聊',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
actions: [],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Expanded(
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
onLoad: () async {
if (hasMore) {
await getData();
}
},
childBuilder: (context, physics) {
return ListView.builder(
physics: physics,
itemCount: dataList.length,
itemBuilder: (context, index) {
final item = dataList[index];
return Ink(
key: ValueKey(item.groupID),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// + +
Expanded(
child: InkWell(
onTap: () async {
// ,
final res = await ImService.instance.getConversation(conversationID: 'group_${item.groupID}');
if (res.success) {
V2TimConversation conversation = res.data;
logger.w(conversation.toLogString());
conversation.showName = conversation.showName ?? item.groupName;
Get.toNamed(
'/chatGroup',
arguments: conversation,
);
}
},
child: Row(
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: item.faceUrl,
width: 50,
height: 50,
placeholderAsset: 'assets/images/group.png',
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.groupName?.isNotEmpty == true ? item.groupName! : '群聊',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
if (item.introduction?.isNotEmpty ?? false) ...[
const SizedBox(height: 2.0),
Text(
item.introduction!,
style: const TextStyle(
color: Colors.grey,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

View File

@ -2,14 +2,17 @@
library; library;
import 'package:card_swiper/card_swiper.dart'; import 'package:card_swiper/card_swiper.dart';
import 'package:dynamic_tabbar/dynamic_tabbar.dart';
import 'package:easy_refresh/easy_refresh.dart'; import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/im_friend_listeners.dart';
import 'package:loopin/components/backtop.dart'; import 'package:loopin/components/backtop.dart';
import 'package:loopin/components/loading.dart'; import 'package:loopin/components/loading.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/controller/shop_index_controller.dart'; import 'package:loopin/controller/shop_index_controller.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/index.dart';
class IndexPage extends StatefulWidget { class IndexPage extends StatefulWidget {
@ -127,15 +130,64 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
// //
Expanded( Expanded(
child: controller.tabController == null child: controller.tabController == null
? const Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: TabBarView( : Obx(
controller: controller.tabController, () {
children: List.generate( final tabs = controller.tabList.asMap().entries.map((entry) {
controller.tabList.length, final idx = entry.key;
(index) => _buildTabContent(index), final item = entry.value;
return TabData(
index: idx,
title: Tab(
child: Center(
child: Text(
item['name'] ?? '',
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
softWrap: false,
), ),
), ),
), ),
content: _buildTabContent(idx),
);
}).toList();
return DynamicTabBarWidget(
onTabControllerUpdated: (tabController) {
controller.tabController = tabController;
//
if (tabController.index != 0) {
tabController.animateTo(0);
}
},
onTabChanged: (index) {
logger.w("当前选中tab: $index");
},
dynamicTabs: tabs,
tabAlignment: TabAlignment.start,
isScrollable: true,
showNextIcon: false, //
showBackIcon: false, // 退
labelColor: FStyle.primaryColor,
indicatorColor: FStyle.primaryColor,
overlayColor: WidgetStateProperty.all(Colors.transparent),
unselectedLabelColor: Colors.black87,
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
),
unselectedLabelStyle: const TextStyle(
fontSize: 14.0,
fontFamily: 'Microsoft YaHei',
),
labelStyle: const TextStyle(
fontSize: 14.0,
fontFamily: 'Microsoft YaHei',
fontWeight: FontWeight.bold,
),
dividerHeight: 0,
);
},
),
),
], ],
), ),
floatingActionButton: Obx(() { floatingActionButton: Obx(() {
@ -156,7 +208,6 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
if (controller.tabController == null) { if (controller.tabController == null) {
return SizedBox(); return SizedBox();
} }
return Column( return Column(
children: [ children: [
// //
@ -186,48 +237,6 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
), ),
); );
}), }),
// TabBar
Obx(() {
int tabCount = controller.tabList.length;
double screenWidth = MediaQuery.of(context).size.width;
double minTabWidth = 60;
bool isScrollable = tabCount * minTabWidth > screenWidth;
return Container(
color: Colors.white,
child: TabBar(
controller: controller.tabController,
tabs: controller.tabList.map((item) {
return Tab(
child: Text(
item['name'] ?? '',
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
softWrap: false,
),
);
}).toList(),
isScrollable: isScrollable,
overlayColor: WidgetStateProperty.all(Colors.transparent),
unselectedLabelColor: Colors.black87,
labelColor: const Color.fromARGB(255, 236, 108, 49),
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
),
unselectedLabelStyle: const TextStyle(
fontSize: 14.0,
fontFamily: 'Microsoft YaHei',
),
labelStyle: const TextStyle(
fontSize: 16.0,
fontFamily: 'Microsoft YaHei',
fontWeight: FontWeight.bold,
),
dividerHeight: 0,
),
);
}),
], ],
); );
} }

View File

@ -554,6 +554,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
return InkWell( return InkWell(
onTap: () { onTap: () {
// //
Get.toNamed('/videoDetail', arguments: {'videoId': item['id']});
}, },
onLongPress: () { onLongPress: () {
showModalBottomSheet( showModalBottomSheet(

View File

@ -6,18 +6,19 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_core.dart'; import 'package:loopin/IM/im_core.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart' hide logger; import 'package:loopin/IM/im_service.dart' hide logger;
import 'package:loopin/api/video_api.dart'; import 'package:loopin/api/video_api.dart';
import 'package:loopin/components/my_toast.dart'; import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/share_type.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
import 'package:loopin/utils/download_video.dart'; import 'package:loopin/utils/download_video.dart';
import 'package:loopin/utils/permissions.dart'; import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/wxsdk.dart'; import 'package:loopin/utils/wxsdk.dart';
import 'package:loopin/models/share_type.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart'; import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart'; import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
@ -497,15 +498,19 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
void handlCoverClick(V2TimConversation conv) async { void handlCoverClick(V2TimConversation conv) async {
// VideoMsg, // VideoMsg,
final userId = conv.userID; final userId = conv.userID;
logger.w(videoData);
final String url = videoData['url']; final String url = videoData['url'];
final img = videoData['firstFrameImg']; final img = (videoData['cover'] != null && videoData['cover'].toString().isNotEmpty) ? videoData['cover'] : videoData['firstFrameImg'];
final width = videoData['width']; final width = videoData['width'];
final height = videoData['height']; final height = videoData['height'];
final videoId = videoData['id'];
final makeJson = jsonEncode({ final makeJson = jsonEncode({
"width": width, "width": width,
"height": height, "height": height,
"imgUrl": img, "imgUrl": img,
"videoUrl": url, "videoUrl": url,
"videoId": videoId,
"userID": Get.find<ImUserInfoController>().userID.value,
}); });
final res = await IMMessage().createCustomMessage( final res = await IMMessage().createCustomMessage(
data: makeJson, data: makeJson,
@ -570,7 +575,9 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
), ),
), ),
body: Stack( body: SafeArea(
bottom: true,
child: Stack(
children: [ children: [
// //
Positioned.fill( Positioned.fill(
@ -908,6 +915,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -9,18 +9,19 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_core.dart'; import 'package:loopin/IM/im_core.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart' hide logger; import 'package:loopin/IM/im_service.dart' hide logger;
import 'package:loopin/api/video_api.dart'; import 'package:loopin/api/video_api.dart';
import 'package:loopin/components/my_toast.dart'; import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/share_type.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
import 'package:loopin/utils/download_video.dart'; import 'package:loopin/utils/download_video.dart';
import 'package:loopin/utils/permissions.dart'; import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/wxsdk.dart'; import 'package:loopin/utils/wxsdk.dart';
import 'package:loopin/models/share_type.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart'; import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart'; import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
@ -982,15 +983,20 @@ class _FriendModuleState extends State<FriendModule> {
void handlCoverClick(V2TimConversation conv) async { void handlCoverClick(V2TimConversation conv) async {
// VideoMsg, // VideoMsg,
final userId = conv.userID; final userId = conv.userID;
final String url = videoList[videoModuleController.videoPlayIndex.value]['url']; final currentVideo = videoList[videoModuleController.videoPlayIndex.value];
final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg']; logger.w(currentVideo);
final width = videoList[videoModuleController.videoPlayIndex.value]['width']; final img = (currentVideo['cover'] != null && currentVideo['cover'].toString().isNotEmpty) ? currentVideo['cover'] : currentVideo['firstFrameImg'];
final height = videoList[videoModuleController.videoPlayIndex.value]['height']; final url = currentVideo['url'];
final width = currentVideo['width'];
final height = currentVideo['height'];
final videoId = currentVideo['id'];
final makeJson = jsonEncode({ final makeJson = jsonEncode({
"width": width, "width": width,
"height": height, "height": height,
"imgUrl": img, "imgUrl": img,
"videoUrl": url, "videoUrl": url,
"videoId": videoId,
"userID": Get.find<ImUserInfoController>().userID.value,
}); });
final res = await IMMessage().createCustomMessage( final res = await IMMessage().createCustomMessage(
data: makeJson, data: makeJson,
@ -1268,13 +1274,10 @@ class _FriendModuleState extends State<FriendModule> {
onTap: () async { onTap: () async {
player.pause(); player.pause();
// //
final result = await Get.toNamed( final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
'/report',
arguments: videoList[videoModuleController
.videoPlayIndex.value]);
if (result != null) { if (result != null) {
player.play(); player.play();
}; }
}, },
), ),
], ],
@ -1312,8 +1315,9 @@ class _FriendModuleState extends State<FriendModule> {
Text( Text(
text, text,
maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3, maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3,
overflow: overflow: videoList[videoModuleController.videoPlayIndex.value]['expanded']
videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? TextOverflow.visible : TextOverflow.ellipsis, ? TextOverflow.visible
: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white, fontSize: 14.0), style: const TextStyle(color: Colors.white, fontSize: 14.0),
), ),
if (isOverflow) if (isOverflow)

View File

@ -9,6 +9,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_core.dart'; import 'package:loopin/IM/im_core.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart' hide logger; import 'package:loopin/IM/im_service.dart' hide logger;
@ -16,12 +17,12 @@ import 'package:loopin/api/video_api.dart';
import 'package:loopin/components/my_toast.dart'; import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/conversation_type.dart'; import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/models/share_type.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
import 'package:loopin/utils/download_video.dart'; import 'package:loopin/utils/download_video.dart';
import 'package:loopin/utils/permissions.dart'; import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/wxsdk.dart'; import 'package:loopin/utils/wxsdk.dart';
import 'package:loopin/models/share_type.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart'; import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart'; import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
@ -980,15 +981,20 @@ class _RecommendModuleState extends State<RecommendModule> {
void handlCoverClick(V2TimConversation conv) async { void handlCoverClick(V2TimConversation conv) async {
// VideoMsg, // VideoMsg,
final userId = conv.userID; final userId = conv.userID;
final String url = videoList[videoModuleController.videoPlayIndex.value]['url']; final currentVideo = videoList[videoModuleController.videoPlayIndex.value];
final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg']; logger.w(currentVideo);
final width = videoList[videoModuleController.videoPlayIndex.value]['width']; final img = (currentVideo['cover'] != null && currentVideo['cover'].toString().isNotEmpty) ? currentVideo['cover'] : currentVideo['firstFrameImg'];
final height = videoList[videoModuleController.videoPlayIndex.value]['height']; final url = currentVideo['url'];
final width = currentVideo['width'];
final height = currentVideo['height'];
final videoId = currentVideo['id'];
final makeJson = jsonEncode({ final makeJson = jsonEncode({
"width": width, "width": width,
"height": height, "height": height,
"imgUrl": img, "imgUrl": img,
"videoUrl": url, "videoUrl": url,
"videoId": videoId,
"userID": Get.find<ImUserInfoController>().userID.value,
}); });
final res = await IMMessage().createCustomMessage( final res = await IMMessage().createCustomMessage(
data: makeJson, data: makeJson,

View File

@ -10,6 +10,7 @@ import 'package:loopin/pages/chat/chat_no_friend.dart';
import 'package:loopin/pages/chat/notify/interaction.dart'; import 'package:loopin/pages/chat/notify/interaction.dart';
import 'package:loopin/pages/chat/notify/noFriend.dart'; import 'package:loopin/pages/chat/notify/noFriend.dart';
import 'package:loopin/pages/chat/notify/system.dart'; import 'package:loopin/pages/chat/notify/system.dart';
import 'package:loopin/pages/groupChat/groupList.dart';
import 'package:loopin/pages/groupChat/index.dart'; import 'package:loopin/pages/groupChat/index.dart';
import 'package:loopin/pages/my/des.dart'; import 'package:loopin/pages/my/des.dart';
import 'package:loopin/pages/my/fans.dart'; import 'package:loopin/pages/my/fans.dart';
@ -67,8 +68,8 @@ final Map<String, Widget> routes = {
'/flow': const Flowing(), '/flow': const Flowing(),
'/eachFlow': const MutualFollowers(), '/eachFlow': const MutualFollowers(),
// //
'/group': const StartGroupChatPage(), '/group': const StartGroupChatPage(), //
// '/groupList': const Grouplist(), //
}; };
final List<GetPage> routeList = routes.entries final List<GetPage> routeList = routes.entries

View File

@ -51,10 +51,24 @@ String parseMessageSummary(V2TimMessage msg) {
// groupNotifyLeaveUp, // -> // groupNotifyLeaveUp, // ->
String _parseCustomMessage(V2TimMessage? msg) { String _parseCustomMessage(V2TimMessage? msg) {
if (msg == null) return '[null]'; if (msg == null) return '[null]';
final sum = msg.cloudCustomData; String sum;
// JSONcloudCustomData的类型为Stringstring变为json
final raw = msg.cloudCustomData;
if (raw == null) {
sum = '';
} else {
try {
final decoded = jsonDecode(raw);
sum = decoded is String ? decoded : raw;
} catch (_) {
sum = raw;
}
}
// final sum = jsonDecode(msg.cloudCustomData!);
final elment = msg.customElem; // final elment = msg.customElem; //
logger.w('解析自定义消息:$sum,自定义属性:${msg.cloudCustomData}'); // logger.w('解析自定义消息:$sum,自定义属性:${msg.cloudCustomData}');
// logger.w('解析element${elment?.desc ?? 'summary_error'}'); logger.w(sum);
logger.w('解析element${elment?.desc ?? 'summary_error'}');
try { try {
switch (sum) { switch (sum) {
case SummaryType.hongbao: case SummaryType.hongbao:

View File

@ -249,6 +249,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
dynamic_tabbar:
dependency: "direct main"
description:
name: dynamic_tabbar
sha256: "017be3705f70e353e579b7a7d56bb19b5c112072b4532bc8bd68767c4a6fc3fe"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.9"
easy_refresh: easy_refresh:
dependency: "direct main" dependency: "direct main"
description: description:
@ -598,69 +606,69 @@ packages:
source: hosted source: hosted
version: "4.5.4" version: "4.5.4"
image_picker: image_picker:
dependency: transitive dependency: "direct main"
description: description:
name: image_picker name: image_picker
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.2" version: "1.2.0"
image_picker_android: image_picker_android:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "6fae381e6af2bbe0365a5e4ce1db3959462fa0c4d234facf070746024bb80c8d" sha256: "28f3987ca0ec702d346eae1d90eda59603a2101b52f1e234ded62cff1d5cfa6e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12+24" version: "0.8.13+1"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.6" version: "3.1.0"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12+2" version: "0.8.13"
image_picker_linux: image_picker_linux:
dependency: transitive dependency: transitive
description: description:
name: image_picker_linux name: image_picker_linux
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.1+2" version: "0.2.2"
image_picker_macos: image_picker_macos:
dependency: transitive dependency: transitive
description: description:
name: image_picker_macos name: image_picker_macos
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.1+2" version: "0.2.2"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.10.1" version: "2.11.0"
image_picker_windows: image_picker_windows:
dependency: transitive dependency: transitive
description: description:
name: image_picker_windows name: image_picker_windows
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.1+1" version: "0.2.2"
install_plugin: install_plugin:
dependency: "direct main" dependency: "direct main"
description: description:
@ -846,7 +854,7 @@ packages:
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
mime: mime:
dependency: transitive dependency: "direct main"
description: description:
name: mime name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
@ -1419,7 +1427,7 @@ packages:
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
video_player: video_player:
dependency: transitive dependency: "direct main"
description: description:
name: video_player name: video_player
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a" sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"

View File

@ -61,6 +61,7 @@ dependencies:
photo_view: ^0.15.0 photo_view: ^0.15.0
shirne_dialog: ^4.8.3 shirne_dialog: ^4.8.3
package_info_plus: ^8.3.0 package_info_plus: ^8.3.0
dynamic_tabbar: ^1.0.9 #tab
flutter_upgrader: ^1.1.20 #更新 flutter_upgrader: ^1.1.20 #更新
path_provider: ^2.1.2 path_provider: ^2.1.2
@ -88,6 +89,9 @@ dependencies:
audioplayers: ^6.5.0 #音频播放 audioplayers: ^6.5.0 #音频播放
flutter_html: ^3.0.0 flutter_html: ^3.0.0
timer_count_down: ^2.2.2 #倒计时 timer_count_down: ^2.2.2 #倒计时
image_picker: ^1.2.0 #相机
video_player: ^2.10.0 #视频处理
mime: ^2.0.0 #文件类型推断
dev_dependencies: dev_dependencies:
flutter_launcher_icons: ^0.13.1 # 使用最新版本 flutter_launcher_icons: ^0.13.1 # 使用最新版本