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,10 @@
import 'package:get/get.dart';
import 'friend_setup_logic.dart';
class FriendSetupBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => FriendSetupLogic());
}
}
@@ -0,0 +1,128 @@
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../../../../routes/app_navigator.dart';
import '../../../chat/chat_logic.dart';
import '../../../conversation/conversation_logic.dart';
import '../../select_contacts/select_contacts_logic.dart';
import '../user_profile _panel_logic.dart';
class FriendSetupLogic extends GetxController {
final conversationLogic = Get.find<ConversationLogic>();
final userProfilesLogic = Get.find<UserProfilePanelLogic>(tag: GetTags.userProfile);
late String userID;
@override
void onInit() {
userID = Get.arguments['userID'];
super.onInit();
}
void toggleBlacklist() {
if (userProfilesLogic.userInfo.value.isBlacklist == true) {
removeBlacklist();
} else {
addBlacklist();
}
}
void addBlacklist() async {
var confirm = await Get.dialog(CustomDialog(title: StrRes.areYouSureAddBlacklist));
if (confirm == true) {
await OpenIM.iMManager.friendshipManager.addBlacklist(
userID: userProfilesLogic.userInfo.value.userID!,
);
userProfilesLogic.userInfo.update((val) {
val?.isBlacklist = true;
});
}
}
void removeBlacklist() async {
await OpenIM.iMManager.friendshipManager.removeBlacklist(
userID: userProfilesLogic.userInfo.value.userID!,
);
userProfilesLogic.userInfo.update((val) {
val?.isBlacklist = false;
});
}
void deleteFromFriendList() async {
var confirm = await Get.dialog(CustomDialog(
title: StrRes.areYouSureDelFriend,
rightText: StrRes.delete,
));
if (confirm) {
await LoadingView.singleton.wrap(asyncFunction: () async {
await OpenIM.iMManager.friendshipManager.deleteFriend(
userID: userProfilesLogic.userInfo.value.userID!,
);
userProfilesLogic.userInfo.update((val) {
val?.isFriendship = false;
});
final userIDList = [
userProfilesLogic.userInfo.value.userID,
OpenIM.iMManager.userID,
];
userIDList.sort();
final conversationID = 'si_${userIDList.join('_')}';
await OpenIM.iMManager.conversationManager.deleteConversationAndDeleteAllMsg(conversationID: conversationID);
conversationLogic.list.removeWhere((e) => e.conversationID == conversationID);
});
if (userProfilesLogic.offAllWhenDelFriend == true) {
AppNavigator.startBackMain();
} else {
Get.back();
}
}
}
recommendToFriend() async {
final isRegistered = Get.isRegistered<ChatLogic>(tag: GetTags.chat);
if (isRegistered) {
final logic = Get.find<ChatLogic>(tag: GetTags.chat);
logic.recommendFriendCarte(UserInfo.fromJson(userProfilesLogic.userInfo.value.toJson()));
return;
}
final result = await AppNavigator.startSelectContacts(
action: SelAction.recommend,
ex: '[${StrRes.carte}]${userProfilesLogic.userInfo.value.nickname}',
);
if (null != result) {
final customEx = result['customEx'];
final checkedList = result['checkedList'];
for (var info in checkedList) {
final userID = IMUtils.convertCheckedToUserID(info);
final groupID = IMUtils.convertCheckedToGroupID(info);
if (customEx is String && customEx.isNotEmpty) {
OpenIM.iMManager.messageManager.sendMessage(
message: await OpenIM.iMManager.messageManager.createTextMessage(
text: customEx,
),
userID: userID,
groupID: groupID,
offlinePushInfo: Config.offlinePushInfo,
);
}
OpenIM.iMManager.messageManager.sendMessage(
message: await OpenIM.iMManager.messageManager.createCardMessage(
userID: userProfilesLogic.userInfo.value.userID!,
nickname: userProfilesLogic.userInfo.value.showName,
faceURL: userProfilesLogic.userInfo.value.faceURL,
),
userID: userID,
groupID: groupID,
offlinePushInfo: Config.offlinePushInfo,
);
}
}
}
void setFriendRemark() => AppNavigator.startSetFriendRemark();
}
@@ -0,0 +1,105 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'friend_setup_logic.dart';
class FriendSetupPage extends StatelessWidget {
final logic = Get.find<FriendSetupLogic>();
FriendSetupPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: StrRes.friendSetup,
),
backgroundColor: Styles.c_F8F9FA,
body: Column(
children: [
10.verticalSpace,
_buildItemView(
label: StrRes.setupRemark,
borderRadius: BorderRadius.only(
topRight: Radius.circular(6.r),
topLeft: Radius.circular(6.r),
),
showRightArrow: true,
onTap: logic.setFriendRemark,
),
_buildItemView(
label: StrRes.recommendToFriend,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(6.r),
bottomRight: Radius.circular(6.r),
),
showRightArrow: true,
onTap: logic.recommendToFriend,
),
10.verticalSpace,
Obx(() => _buildItemView(
label: StrRes.addToBlacklist,
showSwitchButton: true,
switchOn:
logic.userProfilesLogic.userInfo.value.isBlacklist == true,
onChanged: (_) => logic.toggleBlacklist(),
)),
10.verticalSpace,
_buildItemView(
isDelFriendButton: true,
onTap: logic.deleteFromFriendList,
)
],
),
);
}
Widget _buildItemView({
String? label,
bool showRightArrow = false,
bool showSwitchButton = false,
BorderRadius? borderRadius,
bool switchOn = false,
bool isDelFriendButton = false,
ValueChanged<bool>? onChanged,
Function()? onTap,
}) =>
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
child: Ink(
height: 46.h,
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: borderRadius ?? BorderRadius.circular(6.r),
),
child: InkWell(
onTap: onTap,
child: Container(
alignment: isDelFriendButton ? Alignment.center : null,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: isDelFriendButton
? (StrRes.unfriend.toText..style = Styles.ts_FF381F_17sp)
: Row(
children: [
(label ?? '').toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
if (showRightArrow)
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
if (showSwitchButton)
CupertinoSwitch(
value: switchOn,
onChanged: onChanged,
activeColor: Styles.c_0089FF,
),
],
),
),
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'personal_info_logic.dart';
class PersonalInfoBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => PersonalInfoLogic());
}
}
@@ -0,0 +1,106 @@
import 'package:collection/collection.dart';
import 'package:common_utils/common_utils.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:url_launcher/url_launcher.dart';
import '../user_profile _panel_logic.dart';
class PersonalInfoLogic extends GetxController {
final userProfilesLogic = Get.find<UserProfilePanelLogic>(tag: GetTags.userProfile);
late String userID;
final userFullInfo = UserFullInfo().obs;
@override
void onInit() {
userID = Get.arguments['userID'];
super.onInit();
}
@override
void onReady() {
_queryUserFullInfo();
super.onReady();
}
void _queryUserFullInfo() async {
final existUser = UserCacheManager().getUserInfo(userID);
if (existUser != null) {
userFullInfo.update((val) {
val?.nickname = existUser.nickname;
val?.faceURL = existUser.faceURL;
val?.status = existUser.status;
val?.level = existUser.level;
val?.phoneNumber = existUser.phoneNumber;
val?.areaCode = existUser.areaCode;
val?.birth = existUser.birth;
val?.email = existUser.email;
val?.gender = existUser.gender;
val?.mobile = existUser.mobile;
});
}
final list = await LoadingView.singleton.wrap(
asyncFunction: () => Apis.getUserFullInfo(userIDList: [userID]),
);
final info = list?.firstOrNull;
if (null != info) {
UserCacheManager().addOrUpdateUserInfo(userID, info);
userFullInfo.update((val) {
val?.nickname = info.nickname;
val?.faceURL = info.faceURL;
val?.gender = info.gender;
val?.englishName = info.englishName;
val?.birth = info.birth;
val?.telephone = info.telephone;
val?.phoneNumber = info.phoneNumber;
val?.email = info.email;
});
}
}
String? get nickname => IMUtils.emptyStrToNull(userProfilesLogic.userInfo.value.nickname) ?? IMUtils.emptyStrToNull(userFullInfo.value.nickname);
String? get faceURL => IMUtils.emptyStrToNull(userProfilesLogic.userInfo.value.faceURL) ?? IMUtils.emptyStrToNull(userFullInfo.value.faceURL);
bool get isMale => (userProfilesLogic.userInfo.value.gender ?? userFullInfo.value.gender) == 1;
String? get englishName => IMUtils.emptyStrToNull(userFullInfo.value.englishName) ?? '-';
int? get _birth => userProfilesLogic.userInfo.value.birth ?? userFullInfo.value.birth;
String? get birth => _birth == null ? '-' : DateUtil.formatDateMs(_birth!, format: IMUtils.getTimeFormat1());
String? get telephone => IMUtils.emptyStrToNull(userFullInfo.value.telephone) ?? '-';
String? get phoneNumber => IMUtils.emptyStrToNull(userFullInfo.value.phoneNumber) ?? '-';
String? get email => IMUtils.emptyStrToNull(userFullInfo.value.email) ?? '-';
clickPhoneNumber() => _callSystemPhone(userFullInfo.value.phoneNumber);
clickTel() => _callSystemPhone(userFullInfo.value.telephone);
clickEmail() => _callSystemEmail(userFullInfo.value.email);
_callSystemPhone(String? phone) async {
final value = IMUtils.emptyStrToNull(phone);
if (null != value) {
final uri = Uri.parse('tel:$value');
try {
launchUrl(uri);
} catch (_) {}
}
}
_callSystemEmail(String? email) {
final value = IMUtils.emptyStrToNull(email);
if (null != value) {
final uri = Uri.parse('mailto:$value');
try {
launchUrl(uri);
} catch (_) {}
}
}
}
@@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'personal_info_logic.dart';
class PersonalInfoPage extends StatelessWidget {
final logic = Get.find<PersonalInfoLogic>();
PersonalInfoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: StrRes.personalInfo,
),
backgroundColor: Styles.c_F8F9FA,
body: SingleChildScrollView(
child: Obx(
() => Column(
children: [
10.verticalSpace,
_buildCornerBgView(
children: [
_buildItemView(
label: StrRes.avatar,
isAvatar: true,
value: logic.nickname,
url: logic.faceURL,
),
_buildItemView(
label: StrRes.name,
value: logic.nickname,
),
_buildItemView(
label: StrRes.gender,
value: logic.isMale ? StrRes.man : StrRes.woman,
),
_buildItemView(
label: StrRes.englishName,
value: logic.englishName,
),
_buildItemView(
label: StrRes.birthDay,
value: logic.birth,
),
],
),
10.verticalSpace,
_buildCornerBgView(
children: [
_buildItemView(
label: StrRes.mobile,
value: logic.phoneNumber,
onTap: logic.clickPhoneNumber,
),
_buildItemView(
label: StrRes.email,
value: logic.email,
onTap: logic.clickEmail,
),
],
),
],
),
)),
);
}
Widget _buildCornerBgView({required List<Widget> children}) => Container(
padding: EdgeInsets.symmetric(horizontal: 16.w),
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6.r),
topRight: Radius.circular(6.r),
bottomLeft: Radius.circular(6.r),
bottomRight: Radius.circular(6.r),
),
),
child: Column(children: children),
);
Widget _buildItemView({
required String label,
String? value,
String? url,
bool isAvatar = false,
Function()? onTap,
}) =>
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: onTap,
child: SizedBox(
height: 46.h,
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
if (null != value && !isAvatar) value.toText..style = Styles.ts_0C1C33_17sp,
if (isAvatar)
AvatarView(
width: 32.w,
height: 32.h,
url: url,
text: value,
textStyle: Styles.ts_FFFFFF_10sp,
),
],
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'set_remark_logic.dart';
class SetFriendRemarkBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => SetFriendRemarkLogic());
}
}
@@ -0,0 +1,40 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../user_profile _panel_logic.dart';
class SetFriendRemarkLogic extends GetxController {
final userProfilesLogic =
Get.find<UserProfilePanelLogic>(tag: GetTags.userProfile);
late TextEditingController inputCtrl;
void save() async {
try {
await LoadingView.singleton.wrap(
asyncFunction: () => OpenIM.iMManager.friendshipManager.setFriendRemark(
userID: userProfilesLogic.userInfo.value.userID!,
remark: inputCtrl.text.trim(),
),
);
IMViews.showToast(StrRes.saveSuccessfully);
Get.back(result: inputCtrl.text.trim());
} catch (_) {
IMViews.showToast(StrRes.saveFailed);
}
}
@override
void onInit() {
inputCtrl =
TextEditingController(text: userProfilesLogic.userInfo.value.remark);
super.onInit();
}
@override
void onClose() {
inputCtrl.dispose();
super.onClose();
}
}
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'set_remark_logic.dart';
class SetFriendRemarkPage extends StatelessWidget {
final logic = Get.find<SetFriendRemarkLogic>();
SetFriendRemarkPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: StrRes.remark,
right: StrRes.save.toText
..style = Styles.ts_0C1C33_17sp
..onTap = logic.save,
),
backgroundColor: Styles.c_FFFFFF,
body: Column(
children: [
22.verticalSpace,
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_E8EAEF,
borderRadius: BorderRadius.circular(4.r),
),
child: TextField(
controller: logic.inputCtrl,
style: Styles.ts_0C1C33_17sp,
autofocus: true,
inputFormatters: [LengthLimitingTextInputFormatter(16)],
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.symmetric(
vertical: 10.h,
horizontal: 12.w,
),
),
),
),
],
),
);
}
}
@@ -0,0 +1,11 @@
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'user_profile _panel_logic.dart';
class UserProfilePanelBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => UserProfilePanelLogic(), tag: GetTags.userProfile);
}
}
@@ -0,0 +1,373 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:common_utils/common_utils.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim/routes/app_navigator.dart';
import 'package:openim_common/openim_common.dart';
import 'package:sprintf/sprintf.dart';
import 'package:openim_live/openim_live.dart';
import '../../../core/controller/app_controller.dart';
import '../../../core/controller/im_controller.dart';
import '../../conversation/conversation_logic.dart';
class UserProfilePanelLogic extends GetxController {
final appLogic = Get.find<AppController>();
final imLogic = Get.find<IMController>();
final conversationLogic = Get.find<ConversationLogic>();
late Rx<UserFullInfo> userInfo;
GroupMembersInfo? groupMembersInfo;
GroupInfo? groupInfo;
String? groupID;
bool? offAllWhenDelFriend = false;
bool? forceCanAdd = false;
final iHasMutePermissions = false.obs;
final iAmOwner = false.obs;
final mutedTime = "".obs;
final onlineStatus = false.obs;
final onlineStatusDesc = ''.obs;
final groupUserNickname = "".obs;
final joinGroupTime = 0.obs;
final joinGroupMethod = ''.obs;
final hasAdminPermission = false.obs;
final notAllowLookGroupMemberProfiles = true.obs;
final notAllowAddGroupMemberFriend = false.obs;
final iHaveAdminOrOwnerPermission = false.obs;
late StreamSubscription _friendAddedSub;
late StreamSubscription _friendDeletedSub;
late StreamSubscription _friendInfoChangedSub;
late StreamSubscription _memberInfoChangedSub;
@override
void onClose() {
_friendAddedSub.cancel();
_friendInfoChangedSub.cancel();
_memberInfoChangedSub.cancel();
_friendDeletedSub.cancel();
super.onClose();
}
@override
void onInit() {
userInfo = (UserFullInfo()
..userID = Get.arguments['userID']
..nickname = Get.arguments['nickname']
..faceURL = Get.arguments['faceURL'])
.obs;
groupID = Get.arguments['groupID'];
offAllWhenDelFriend = Get.arguments['offAllWhenDelFriend'];
forceCanAdd = Get.arguments['forceCanAdd'];
_friendAddedSub = imLogic.friendAddSubject.listen((user) {
if (user.userID == userInfo.value.userID) {
userInfo.update((val) {
val?.isFriendship = true;
});
}
});
_friendDeletedSub = imLogic.friendDelSubject.listen((user) {
if (user.userID == userInfo.value.userID) {
UserCacheManager().removeUserInfo(user.userID!);
_getUsersInfo();
}
});
_friendInfoChangedSub = imLogic.friendInfoChangedSubject.listen((user) {
if (user.userID == userInfo.value.userID) {
userInfo.update((val) {
val?.nickname = user.nickname;
val?.remark = user.remark;
});
UserCacheManager().addOrUpdateUserInfo(user.userID!, userInfo.value);
}
});
_memberInfoChangedSub = imLogic.memberInfoChangedSubject.listen((value) {
if (value.userID == userInfo.value.userID) {
if (null != value.muteEndTime) {
_calMuteTime(value.muteEndTime!);
}
groupUserNickname.value = value.nickname ?? '';
}
});
super.onInit();
}
@override
void onReady() {
_getUsersInfo();
_queryGroupInfo();
_queryGroupMemberInfo();
super.onReady();
}
bool get isMyself => userInfo.value.userID == OpenIM.iMManager.userID;
bool get isGroupMemberPage => null != groupID && groupID!.isNotEmpty;
bool get isFriendship => userInfo.value.isFriendship == true;
bool get isAllowAddFriend => userInfo.value.allowAddFriend == 1;
bool get allowSendMsgNotFriend {
final r = null == appLogic.clientConfigMap['allowSendMsgNotFriend'] ||
appLogic.clientConfigMap['allowSendMsgNotFriend'] == '1';
return r;
}
void _getUsersInfo() async {
final userID = userInfo.value.userID!;
final existUser = UserCacheManager().getUserInfo(userID);
if (existUser != null) {
userInfo.update((val) {
val?.nickname = existUser.nickname;
val?.faceURL = existUser.faceURL;
val?.status = existUser.status;
val?.level = existUser.level;
val?.phoneNumber = existUser.phoneNumber;
val?.areaCode = existUser.areaCode;
val?.birth = existUser.birth;
val?.email = existUser.email;
val?.gender = existUser.gender;
val?.mobile = existUser.mobile;
});
}
if (userID == OpenIM.iMManager.userID) {
final user = await OpenIM.iMManager.userManager.getSelfUserInfo();
userInfo.update((val) {
val?.nickname = user.nickname;
val?.faceURL = user.faceURL;
});
UserCacheManager().addOrUpdateUserInfo(userID, userInfo.value);
return;
}
final friendInfo = (await OpenIM.iMManager.friendshipManager.getFriendsInfo(
userIDList: [userID],
))
.firstOrNull;
final blackList = await OpenIM.iMManager.friendshipManager.getBlacklist();
final isFriendship = friendInfo != null;
final isBlack = blackList.firstWhereOrNull((e) => e.userID == friendInfo?.userID) != null;
if (friendInfo == null) {
final user = (await OpenIM.iMManager.userManager.getUsersInfoWithCache(
[userID],
))
.firstOrNull;
if (user != null) {
userInfo.update((val) {
val?.nickname = user.nickname;
val?.faceURL = user.faceURL;
val?.remark = friendInfo?.remark;
val?.isBlacklist = isBlack;
val?.isFriendship = isFriendship;
});
}
} else {
userInfo.update((val) {
val?.nickname = friendInfo.nickname;
val?.faceURL = friendInfo.faceURL;
val?.remark = friendInfo.remark;
val?.isBlacklist = isBlack;
val?.isFriendship = isFriendship;
});
}
UserCacheManager().addOrUpdateUserInfo(userID, userInfo.value);
final list2 = await Apis.getUserFullInfo(userIDList: [userID]);
final fullInfo = list2?.firstOrNull;
if (null != fullInfo) {
userInfo.update((val) {
val?.allowAddFriend = fullInfo.allowAddFriend;
val?.status = fullInfo.status;
val?.level = fullInfo.level;
val?.phoneNumber = fullInfo.phoneNumber;
val?.areaCode = fullInfo.areaCode;
val?.birth = fullInfo.birth;
val?.email = fullInfo.email;
val?.gender = fullInfo.gender;
val?.mobile = fullInfo.mobile;
val?.nickname = fullInfo.nickname;
val?.faceURL = fullInfo.faceURL;
val?.remark = friendInfo?.remark;
val?.isBlacklist = isBlack;
val?.isFriendship = isFriendship;
});
UserCacheManager().addOrUpdateUserInfo(userID, userInfo.value);
}
}
void _resetAvatar(String url) async {
clearMemoryImageCache(keyToMd5(url));
await clearDiskCachedImage(url);
PaintingBinding.instance.imageCache.evict(keyToMd5(url));
userInfo.refresh();
}
_queryGroupInfo() async {
if (isGroupMemberPage) {
var list = await OpenIM.iMManager.groupManager.getGroupsInfo(
groupIDList: [groupID!],
);
groupInfo = list.firstOrNull;
notAllowLookGroupMemberProfiles.value = groupInfo?.lookMemberInfo == 1;
notAllowAddGroupMemberFriend.value = groupInfo?.applyMemberFriend == 1;
}
}
_queryGroupMemberInfo() async {
if (isGroupMemberPage) {
final list = await OpenIM.iMManager.groupManager.getGroupMembersInfo(
groupID: groupID!,
userIDList: [userInfo.value.userID!, if (!isMyself) OpenIM.iMManager.userID],
);
final other = list.firstWhereOrNull((e) => e.userID == userInfo.value.userID);
groupMembersInfo = other;
groupUserNickname.value = other?.nickname ?? '';
joinGroupTime.value = other?.joinTime ?? 0;
_getJoinGroupMethod(other);
hasAdminPermission.value = other?.roleLevel == GroupRoleLevel.admin;
if (!isMyself) {
var me = list.firstWhereOrNull((e) => e.userID == OpenIM.iMManager.userID);
iAmOwner.value = me?.roleLevel == GroupRoleLevel.owner;
iHasMutePermissions.value = me?.roleLevel == GroupRoleLevel.owner ||
(me?.roleLevel == GroupRoleLevel.admin && other?.roleLevel == GroupRoleLevel.member);
iHaveAdminOrOwnerPermission.value =
me?.roleLevel == GroupRoleLevel.owner || me?.roleLevel == GroupRoleLevel.admin;
}
if (null != other && null != other.muteEndTime && other.muteEndTime! > 0) {
_calMuteTime(other.muteEndTime!);
}
}
}
_getJoinGroupMethod(GroupMembersInfo? other) async {
if (other?.joinSource == 2) {
if (other!.inviterUserID != null && other.inviterUserID != other.userID) {
final list = await OpenIM.iMManager.groupManager.getGroupMembersInfo(
groupID: groupID!,
userIDList: [other.inviterUserID!],
);
var inviterUserInfo = list.firstOrNull;
joinGroupMethod.value = sprintf(
StrRes.byInviteJoinGroup,
[inviterUserInfo?.nickname ?? ''],
);
}
} else if (other?.joinSource == 3) {
joinGroupMethod.value = StrRes.byIDJoinGroup;
} else if (other?.joinSource == 4) {
joinGroupMethod.value = StrRes.byQrcodeJoinGroup;
}
}
_calMuteTime(int time) {
var date = DateUtil.formatDateMs(time, format: IMUtils.getTimeFormat2());
var now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
var diff = time - now;
if (diff > 0) {
mutedTime.value = date;
} else {
mutedTime.value = "";
}
}
String getShowName() {
if (isGroupMemberPage) {
if (isFriendship) {
if (null != IMUtils.emptyStrToNull(userInfo.value.remark)) {
return '${groupUserNickname.value}(${IMUtils.emptyStrToNull(userInfo.value.remark)})';
}
}
if (groupUserNickname.value.isEmpty) {
return userInfo.value.nickname ??= "";
}
return groupUserNickname.value;
}
if (userInfo.value.remark != null && userInfo.value.remark!.isNotEmpty) {
return '${userInfo.value.nickname}(${userInfo.value.remark})';
}
return userInfo.value.nickname ?? '';
}
void toChat() {
conversationLogic.toChat(
userID: userInfo.value.userID,
nickname: userInfo.value.showName,
faceURL: userInfo.value.faceURL,
);
}
void toCall() {
IMViews.openIMCallSheet(userInfo.value.showName, (index) {
imLogic.call(
callObj: CallObj.single,
callType: index == 0 ? CallType.audio : CallType.video,
inviteeUserIDList: [userInfo.value.userID!],
);
});
}
void copyID() {
IMUtils.copy(text: userInfo.value.userID!);
}
void addFriend() => AppNavigator.startSendVerificationApplication(
userID: userInfo.value.userID!,
);
void viewPersonalInfo() => AppNavigator.startPersonalInfo(
userID: userInfo.value.userID!,
);
void friendSetup() => AppNavigator.startFriendSetup(
userID: userInfo.value.userID!,
);
}
class UserCacheManager {
static final UserCacheManager _instance = UserCacheManager._();
UserCacheManager._();
final Map<String, UserFullInfo> _userInfoMap = {};
void addOrUpdateUserInfo(String userID, UserFullInfo userInfo) {
_userInfoMap[userID] = userInfo;
}
UserFullInfo? getUserInfo(String userID) {
return _userInfoMap[userID];
}
void removeUserInfo(String userID) {
_userInfoMap.remove(userID);
}
factory UserCacheManager() {
return _instance;
}
}
@@ -0,0 +1,254 @@
import 'dart:ui';
import 'package:common_utils/common_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'user_profile _panel_logic.dart';
class UserProfilePanelPage extends StatelessWidget {
final logic = Get.find<UserProfilePanelLogic>(tag: GetTags.userProfile);
UserProfilePanelPage({super.key});
@override
Widget build(BuildContext context) {
return Obx(
() => Scaffold(
appBar: TitleBar.back(
right: logic.isFriendship
? (ImageRes.moreBlack.toImage
..width = 24.w
..height = 24.h
..onTap = logic.friendSetup)
: null,
),
backgroundColor: Styles.c_F8F9FA,
body: SizedBox(
height: 1.sh,
child: Stack(
children: [
SingleChildScrollView(
child: Column(
children: [
_buildBaseInfoView(),
if (logic.isGroupMemberPage) _buildEnterGroupMethodView(),
if (logic.isFriendship ||
logic.isMyself ||
logic.isGroupMemberPage && !logic.notAllowLookGroupMemberProfiles.value)
_buildItemView(
label: StrRes.personalInfo,
showRightArrow: true,
onTap: logic.viewPersonalInfo,
),
SizedBox(height: 108.h),
],
),
),
if ((logic.isFriendship || logic.allowSendMsgNotFriend) && !logic.isMyself) _buildButtonGroup(),
],
),
),
),
);
}
Widget _buildBaseInfoView() => Container(
color: Styles.c_FFFFFF,
height: 80.h,
margin: EdgeInsets.only(bottom: 10.h),
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
AvatarView(
url: logic.userInfo.value.faceURL,
text: logic.userInfo.value.nickname,
width: 48.w,
height: 48.h,
textStyle: Styles.ts_FFFFFF_14sp,
enabledPreview: true,
),
12.horizontalSpace,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
logic.getShowName().toText
..style = Styles.ts_0C1C33_17sp_medium
..maxLines = 1
..overflow = TextOverflow.ellipsis,
if (!logic.isGroupMemberPage || logic.isGroupMemberPage && !logic.notAllowAddGroupMemberFriend.value)
Padding(
padding: EdgeInsets.only(top: 4.h),
child: (logic.userInfo.value.userID ?? '').toText
..style = Styles.ts_8E9AB0_14sp
..onTap = logic.copyID,
),
],
),
),
if (!logic.isMyself &&
logic.isAllowAddFriend &&
!logic.isFriendship &&
(!logic.isGroupMemberPage ||
logic.forceCanAdd == true ||
logic.isGroupMemberPage && !logic.notAllowAddGroupMemberFriend.value))
Material(
child: Ink(
decoration: BoxDecoration(
color: Styles.c_0089FF,
borderRadius: BorderRadius.circular(6.r),
),
child: InkWell(
onTap: logic.addFriend,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 9.w,
vertical: 4.h,
),
child: Row(
children: [
ImageRes.addContacts.toImage
..width = 21.w
..height = 21.h
..color = Styles.c_FFFFFF,
2.horizontalSpace,
StrRes.add.toText..style = Styles.ts_FFFFFF_14sp,
],
),
),
),
),
),
],
),
);
Widget _buildEnterGroupMethodView() {
if (logic.joinGroupTime.value == 0 && logic.joinGroupMethod.value.isEmpty) {
return Container();
}
return Container(
color: Styles.c_FFFFFF,
margin: EdgeInsets.only(bottom: 10.h),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
child: Table(
defaultVerticalAlignment: TableCellVerticalAlignment.top,
columnWidths: {0: FixedColumnWidth(100.w)},
children: [
if (logic.joinGroupTime.value > 0)
_buildTabRowView(
label: StrRes.joinGroupDate,
value: DateUtil.formatDateMs(
logic.joinGroupTime.value,
format: DateFormats.zh_y_mo_d,
),
),
if (logic.joinGroupMethod.value.isNotEmpty)
_buildTabRowView(
label: StrRes.joinGroupMethod,
value: logic.joinGroupMethod.value,
),
],
),
);
}
TableRow _buildTabRowView({
required String label,
String? value,
}) =>
TableRow(
children: [
TableCell(
child: Container(
constraints: BoxConstraints(minHeight: 40.h),
alignment: Alignment.centerLeft,
child: label.toText..style = Styles.ts_8E9AB0_17sp,
),
),
TableCell(
child: Container(
constraints: BoxConstraints(minHeight: 40.h),
alignment: Alignment.centerLeft,
child: (value ?? '').toText..style = Styles.ts_0C1C33_17sp,
),
),
],
);
Widget _buildItemView({
required String label,
String? value,
bool addMargin = false,
bool showSwitchButton = false,
bool showRightArrow = false,
bool switchOn = false,
ValueChanged<bool>? onChanged,
Function()? onTap,
}) =>
Container(
margin: EdgeInsets.only(bottom: addMargin ? 10.h : 0),
child: Ink(
color: Styles.c_FFFFFF,
height: 56.h,
child: InkWell(
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
if (showSwitchButton)
CupertinoSwitch(
value: switchOn,
activeColor: Styles.c_0089FF,
onChanged: onChanged,
),
if (null != value) value.toText..style = Styles.ts_0C1C33_17sp,
if (showRightArrow)
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
),
);
Widget _buildButtonGroup() => Positioned(
bottom: 0.h,
width: 1.sw,
child: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
child: Container(
color: Styles.c_F8F9FA.withOpacity(.3),
padding: EdgeInsets.symmetric(horizontal: 9.w),
height: 108.h,
child: Row(
children: [
Expanded(
child: ImageTextButton.call(
onTap: logic.toCall,
),
),
11.horizontalSpace,
Expanded(
child: ImageTextButton.message(
onTap: logic.toChat,
),
),
],
),
),
),
),
);
}