feat(mobile-next): 导入官方 openim-flutter-demo 固定基线 3.8.3-patch.3 并完成双构建

- 上游: github.com/OpenIMSDK/openim-flutter-demo tag 3.8.3-patch.3
  commit b3dfdb1e8aaeaaf6f0793e10cadd20d5c184a31f
- 并行目录 mobile-next/ 原样导入(含 LICENSE),不覆盖现网 mobile/
- 锁定 Flutter 3.24.5 / Dart 3.5.4 / JDK 17 / Gradle 7.6.3 / AGP 7.3.1
- 实测 Android debug 与 release 构建均成功(见 docs/mobile-next-baseline-import.md)
- 本提交可单独回退:git revert 1db1229(重写前)

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
编码工程师
2026-08-19 15:24:33 +08:00
co-authored by multica-agent
parent fa8584a73f
commit 8046ec7483
767 changed files with 57658 additions and 0 deletions
@@ -0,0 +1,438 @@
import 'dart:async';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:synchronized/synchronized.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import '../../../core/controller/app_controller.dart';
import '../../../core/controller/im_controller.dart';
import '../../../routes/app_navigator.dart';
import '../../contacts/select_contacts/select_contacts_logic.dart';
import '../../conversation/conversation_logic.dart';
import '../chat_logic.dart';
import 'edit_name/edit_name_logic.dart';
import 'group_member_list/group_member_list_logic.dart';
class GroupSetupLogic extends GetxController {
final imLogic = Get.find<IMController>();
final chatLogic = Get.find<ChatLogic>(tag: GetTags.chat);
final appLogic = Get.find<AppController>();
final conversationLogic = Get.find<ConversationLogic>();
final memberList = <GroupMembersInfo>[].obs;
late Rx<ConversationInfo> conversationInfo;
late Rx<GroupInfo> groupInfo;
late Rx<GroupMembersInfo> myGroupMembersInfo;
late StreamSubscription _guSub;
late StreamSubscription _mASub;
late StreamSubscription _mISub;
late StreamSubscription _mDSub;
late StreamSubscription _ccSub;
late StreamSubscription _jasSub;
late StreamSubscription _jdsSub;
final lock = Lock();
final isJoinedGroup = false.obs;
final avatar = Rx<File?>(null);
@override
void onInit() async {
if (Get.arguments['conversationInfo'] != null) {
conversationInfo = Rx(Get.arguments['conversationInfo']);
} else {
final temp = await OpenIM.iMManager.conversationManager.getOneConversation(
sourceID: chatLogic.conversationInfo.isGroupChat
? chatLogic.conversationInfo.groupID!
: chatLogic.conversationInfo.userID!,
sessionType: chatLogic.conversationInfo.conversationType!);
conversationInfo = Rx(temp);
}
groupInfo = Rx(_defaultGroupInfo);
myGroupMembersInfo = Rx(_defaultMemberInfo);
_ccSub = imLogic.conversationChangedSubject.listen((newList) {
final newValue =
newList.firstWhereOrNull((element) => element.conversationID == conversationInfo.value.conversationID);
if (newValue != null) {
conversationInfo.update((val) {
val?.isPinned = newValue.isPinned;
val?.recvMsgOpt = newValue.recvMsgOpt;
val?.isMsgDestruct = newValue.isMsgDestruct;
val?.msgDestructTime = newValue.msgDestructTime;
});
}
});
_guSub = imLogic.groupInfoUpdatedSubject.listen((value) {
if (value.groupID == groupInfo.value.groupID) {
_updateGroupInfo(value);
}
});
_jasSub = imLogic.joinedGroupAddedSubject.listen((value) {
if (value.groupID == groupInfo.value.groupID) {
isJoinedGroup.value = true;
_queryAllInfo();
}
});
_jdsSub = imLogic.joinedGroupDeletedSubject.listen((value) {
if (value.groupID == groupInfo.value.groupID) {
isJoinedGroup.value = false;
}
});
_mISub = imLogic.memberInfoChangedSubject.listen((e) {
if (e.groupID == groupInfo.value.groupID && e.userID == myGroupMembersInfo.value.userID) {
myGroupMembersInfo.update((val) {
val?.nickname = e.nickname;
val?.roleLevel = e.roleLevel;
});
}
if (e.groupID == groupInfo.value.groupID && e.userID == groupInfo.value.ownerUserID) {
var index = memberList.indexWhere((element) => element.userID == groupInfo.value.ownerUserID);
if (index == -1) {
memberList.insert(0, e);
} else if (index != 0) {
memberList.insert(0, memberList.removeAt(index));
}
}
memberList.sort((a, b) {
if (b.roleLevel != a.roleLevel) {
return b.roleLevel!.compareTo(a.roleLevel!);
} else {
return b.joinTime!.compareTo(a.joinTime!);
}
});
});
_mASub = imLogic.memberAddedSubject.listen((e) async {
if (e.groupID == groupInfo.value.groupID) {
if (e.userID == OpenIM.iMManager.userID) {
isJoinedGroup.value = true;
_queryAllInfo();
} else {
memberList.add(e);
}
}
});
_mDSub = imLogic.memberDeletedSubject.listen((e) {
if (e.groupID == groupInfo.value.groupID) {
if (e.userID == OpenIM.iMManager.userID) {
isJoinedGroup.value = false;
} else {
memberList.removeWhere((element) => element.userID == e.userID);
}
}
});
super.onInit();
}
@override
void onReady() {
_checkIsJoinedGroup();
super.onReady();
}
@override
void onClose() {
_guSub.cancel();
_mASub.cancel();
_mDSub.cancel();
_ccSub.cancel();
_mISub.cancel();
_jdsSub.cancel();
_jasSub.cancel();
super.onClose();
}
get _defaultGroupInfo => GroupInfo(
groupID: conversationInfo.value.groupID!,
groupName: conversationInfo.value.showName,
faceURL: conversationInfo.value.faceURL,
memberCount: 0,
);
get _defaultMemberInfo => GroupMembersInfo(
userID: OpenIM.iMManager.userID,
nickname: OpenIM.iMManager.userInfo.nickname,
);
bool get isOwnerOrAdmin => isOwner || isAdmin;
bool get isAdmin => myGroupMembersInfo.value.roleLevel == GroupRoleLevel.admin;
bool get isNotDisturb => conversationInfo.value.recvMsgOpt != 0;
bool get isOwner => groupInfo.value.ownerUserID == OpenIM.iMManager.userID;
String get conversationID => conversationInfo.value.conversationID;
void _checkIsJoinedGroup() async {
isJoinedGroup.value = await OpenIM.iMManager.groupManager.isJoinedGroup(
groupID: groupInfo.value.groupID,
);
_queryAllInfo();
}
void _queryAllInfo() {
if (isJoinedGroup.value) {
getGroupInfo();
getGroupMembers();
getMyGroupMemberInfo();
}
}
getGroupMembers() async {
var list = await OpenIM.iMManager.groupManager.getGroupMemberList(
groupID: groupInfo.value.groupID,
count: 10,
);
memberList.assignAll(list);
}
getGroupInfo() async {
var list = await OpenIM.iMManager.groupManager.getGroupsInfo(
groupIDList: [groupInfo.value.groupID],
);
var value = list.firstOrNull;
if (null != value) {
_updateGroupInfo(value);
}
}
getMyGroupMemberInfo() async {
final list = await OpenIM.iMManager.groupManager.getGroupMembersInfo(
groupID: groupInfo.value.groupID,
userIDList: [OpenIM.iMManager.userID],
);
final info = list.firstOrNull;
if (null != info) {
myGroupMembersInfo.update((val) {
val?.nickname = info.nickname;
val?.roleLevel = info.roleLevel;
});
}
}
void _updateGroupInfo(GroupInfo value) {
groupInfo.update((val) {
val?.groupName = value.groupName;
val?.faceURL = value.faceURL;
val?.notification = value.notification;
val?.introduction = value.introduction;
val?.memberCount = value.memberCount;
val?.ownerUserID = value.ownerUserID;
val?.status = value.status;
val?.needVerification = value.needVerification;
val?.groupType = value.groupType;
val?.lookMemberInfo = value.lookMemberInfo;
val?.applyMemberFriend = value.applyMemberFriend;
val?.notificationUserID = value.notificationUserID;
val?.notificationUpdateTime = value.notificationUpdateTime;
val?.ex = value.ex;
});
}
void modifyGroupAvatar() async {
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
Get.context!,
pickerConfig: const AssetPickerConfig(maxAssets: 1, requestType: RequestType.image),
);
if (assets != null) {
final file = await assets.first.file;
final result = await IMViews.uCropPic(file!.path);
final path = result['path'];
final url = result['url'];
if (url != null) {
avatar.value = File(path);
await _modifyGroupInfo(faceUrl: url);
groupInfo.update((val) {
val?.faceURL = url;
});
}
}
}
void modifyGroupName(String? faceUrl) => AppNavigator.startEditGroupName(
type: EditNameType.groupNickname,
faceUrl: faceUrl,
);
_modifyGroupInfo({
String? groupName,
String? notification,
String? introduction,
String? faceUrl,
}) =>
OpenIM.iMManager.groupManager.setGroupInfo(GroupInfo(
groupID: groupInfo.value.groupID,
groupName: groupName,
notification: notification,
introduction: introduction,
faceURL: faceUrl,
));
void viewGroupQrcode() => AppNavigator.startGroupQrcode();
void viewGroupMembers() => AppNavigator.startGroupMemberList(
groupInfo: groupInfo.value,
);
void groupManage() => AppNavigator.startGroupManage(
groupInfo: groupInfo.value,
);
void _removeConversation() async {
await OpenIM.iMManager.conversationManager.deleteConversationAndDeleteAllMsg(
conversationID: conversationInfo.value.conversationID,
);
conversationLogic.removeConversation(conversationInfo.value.conversationID);
}
void quitGroup() async {
if (isJoinedGroup.value) {
if (isOwner) {
var confirm = await Get.dialog(CustomDialog(
title: StrRes.dismissGroupHint,
));
if (confirm == true) {
await OpenIM.iMManager.groupManager.dismissGroup(
groupID: groupInfo.value.groupID,
);
} else {
return;
}
} else {
var confirm = await Get.dialog(CustomDialog(
title: StrRes.quitGroupHint,
));
if (confirm == true) {
await OpenIM.iMManager.groupManager.quitGroup(
groupID: groupInfo.value.groupID,
);
} else {
return;
}
}
} else {
_removeConversation();
}
AppNavigator.startBackMain();
}
void copyGroupID() {
IMUtils.copy(text: groupInfo.value.groupID);
}
int length() {
int buttons = isOwnerOrAdmin ? 2 : 1;
return (memberList.length + buttons) > 10 ? 10 : (memberList.length + buttons);
}
Widget itemBuilder({
required int index,
required Widget Function(GroupMembersInfo info) builder,
required Widget Function() addButton,
required Widget Function() delButton,
}) {
var length = isOwnerOrAdmin ? 8 : 9;
if (memberList.length > length) {
if (index < length) {
var info = memberList.elementAt(index);
return builder(info);
} else if (index == length) {
return addButton();
} else {
return delButton();
}
} else {
if (index < memberList.length) {
var info = memberList.elementAt(index);
return builder(info);
} else if (index == memberList.length) {
return addButton();
} else {
return delButton();
}
}
}
void toggleNotDisturb() {
LoadingView.singleton.wrap(
asyncFunction: () => OpenIM.iMManager.conversationManager.setConversationRecvMessageOpt(
conversationID: conversationID,
status: !isNotDisturb ? 2 : 0,
));
}
void clearChatHistory() async {
var confirm = await Get.dialog(CustomDialog(
title: StrRes.confirmClearChatHistory,
rightText: StrRes.clearAll,
));
if (confirm == true) {
await OpenIM.iMManager.conversationManager.clearConversationAndDeleteAllMsg(
conversationID: conversationID,
);
chatLogic.clearAllMessage();
IMViews.showToast(StrRes.clearSuccessfully);
}
}
addMember() async {
final result = await AppNavigator.startSelectContacts(
action: SelAction.addMember,
groupID: groupInfo.value.groupID,
);
final list = IMUtils.convertSelectContactsResultToUserID(result);
if (list is List<String>) {
try {
await LoadingView.singleton.wrap(
asyncFunction: () => OpenIM.iMManager.groupManager.inviteUserToGroup(
groupID: groupInfo.value.groupID,
userIDList: list,
reason: 'Come on baby',
),
);
} catch (_) {}
getGroupMembers();
}
}
removeMember() async {
final list = await AppNavigator.startGroupMemberList(
groupInfo: groupInfo.value,
opType: GroupMemberOpType.del,
);
if (list is List<GroupMembersInfo>) {
var removeUidList = list.map((e) => e.userID!).toList();
try {
await LoadingView.singleton.wrap(
asyncFunction: () => OpenIM.iMManager.groupManager.kickGroupMember(
groupID: groupInfo.value.groupID,
userIDList: removeUidList,
reason: 'Get out baby',
),
);
} catch (_) {}
getGroupMembers();
}
}
void viewMemberInfo(GroupMembersInfo membersInfo) => AppNavigator.startUserProfilePane(
userID: membersInfo.userID!,
nickname: membersInfo.nickname,
faceURL: membersInfo.faceURL,
groupID: membersInfo.groupID,
);
}