This commit is contained in:
2025-06-30 16:24:20 +08:00
commit 68de5eb048
454 changed files with 56921 additions and 0 deletions
@@ -0,0 +1,183 @@
import 'package:azlistview_all_platforms/azlistview_all_platforms.dart';
import 'package:flutter/material.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_info.dart';
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/az_list_view.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
typedef GroupItemBuilder = Widget Function(BuildContext context, V2TimGroupInfo groupInfo);
class TIMUIKitGroup extends StatefulWidget {
final void Function(V2TimGroupInfo groupInfo, V2TimConversation conversation)? onTapItem;
final Widget Function(BuildContext context)? emptyBuilder;
final GroupItemBuilder? itemBuilder;
/// the filter for group conversation
final bool Function(V2TimGroupInfo? groupInfo)? groupCollector;
const TIMUIKitGroup({Key? key, this.onTapItem, this.emptyBuilder, this.itemBuilder, this.groupCollector})
: super(key: key);
@override
State<StatefulWidget> createState() => _TIMUIKitGroupState();
}
class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
final TUIFriendShipViewModel _friendshipViewModel = serviceLocator<TUIFriendShipViewModel>();
final TUIGroupListenerModel _groupListenerModel = serviceLocator<TUIGroupListenerModel>();
List<ISuspensionBeanImpl<V2TimGroupInfo>> _getShowList(List<V2TimGroupInfo> groupList) {
final List<ISuspensionBeanImpl<V2TimGroupInfo>> showList = List.empty(growable: true);
for (var i = 0; i < groupList.length; i++) {
final item = groupList[i];
final showName = item.groupName ?? item.groupID;
String pinyin = PinyinHelper.getPinyinE(showName);
String tag = pinyin.substring(0, 1).toUpperCase();
if (RegExp("[A-Z]").hasMatch(tag)) {
showList.add(ISuspensionBeanImpl(memberInfo: item, tagIndex: tag));
} else {
showList.add(ISuspensionBeanImpl(memberInfo: item, tagIndex: "#"));
}
}
SuspensionUtil.sortListBySuspensionTag(showList);
return showList;
}
Widget _itemBuilder(BuildContext context, V2TimGroupInfo groupInfo) {
final theme = Provider.of<TUIThemeViewModel>(context).theme;
final showName = groupInfo.groupName ?? groupInfo.groupID;
final faceUrl = groupInfo.faceUrl ?? "";
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
child: Material(
color: isDesktopScreen ? theme.wideBackgroundColor : null,
child: InkWell(
onTap: (() async {
if (widget.onTapItem != null) {
V2TimConversation conversation = V2TimConversation(
conversationID: "group_${groupInfo.groupID}",
groupID: groupInfo.groupID,
type: 2,
showName: groupInfo.groupName,
groupType: groupInfo.groupType,
faceUrl: groupInfo.faceUrl,
);
final res = await TencentImSDKPlugin.v2TIMManager
.getConversationManager()
.getConversation(conversationID: "group_${groupInfo.groupID}");
if (res.code == 0 && res.data != null) {
conversation = res.data!;
}
widget.onTapItem!(groupInfo, conversation);
}
}),
child: Container(
padding: const EdgeInsets.only(top: 10, left: 16),
child: Row(
children: [
Container(
padding: const EdgeInsets.only(bottom: 12),
margin: const EdgeInsets.only(right: 12),
child: SizedBox(
height: isDesktopScreen ? 30 : 40,
width: isDesktopScreen ? 30 : 40,
child: Avatar(
faceUrl: faceUrl,
showName: showName,
type: 2,
),
),
),
Expanded(
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(top: 10, bottom: 20),
child: Text(
showName,
style: TextStyle(color: Colors.black, fontSize: isDesktopScreen ? 14 : 18),
),
))
],
),
),
),
),
);
}
GroupItemBuilder _getItemBuilder() {
return widget.itemBuilder ?? _itemBuilder;
}
@override
void initState() {
super.initState();
_friendshipViewModel.loadGroupListData();
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: _friendshipViewModel),
ChangeNotifierProvider.value(value: _groupListenerModel),
ChangeNotifierProvider.value(value: serviceLocator<TUIThemeViewModel>()),
],
builder: (BuildContext context, Widget? w) {
final NeedUpdate? needUpdate = Provider.of<TUIGroupListenerModel>(context).needUpdate;
if (needUpdate != null) {
_groupListenerModel.needUpdate = null;
switch (needUpdate.updateType) {
case UpdateType.groupInfo:
Provider.of<TUIFriendShipViewModel>(context).loadGroupListData();
break;
case UpdateType.memberList:
Provider.of<TUIFriendShipViewModel>(context).loadGroupListData();
break;
default:
break;
}
}
List<V2TimGroupInfo> groupList = Provider.of<TUIFriendShipViewModel>(context).groupList;
if (widget.groupCollector != null) {
groupList = groupList.where(widget.groupCollector!).toList();
}
if (groupList.isNotEmpty) {
final showList = _getShowList(groupList);
return AZListViewContainer(
isShowIndexBar: false,
memberList: showList,
itemBuilder: (context, index) {
final groupInfo = showList[index].memberInfo;
final itemBuilder = _getItemBuilder();
return itemBuilder(context, groupInfo);
});
}
if (widget.emptyBuilder != null) {
return widget.emptyBuilder!(context);
}
return Container();
},
);
}
}
@@ -0,0 +1,220 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_application_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_application.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
typedef GroupApplicationItemBuilder = Widget Function(
BuildContext context, V2TimGroupApplication applicationInfo, int index);
enum ApplicationStatus {
none,
accept,
reject,
}
class TIMUIKitGroupApplicationList extends StatefulWidget {
/// the builder for the request item
final GroupApplicationItemBuilder? itemBuilder;
/// group ID
final String groupID;
const TIMUIKitGroupApplicationList({Key? key, this.itemBuilder, required this.groupID}) : super(key: key);
@override
State<StatefulWidget> createState() => TIMUIKitGroupApplicationListState();
}
class TIMUIKitGroupApplicationListState extends TIMUIKitState<TIMUIKitGroupApplicationList> {
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
final GroupServices _groupServices = serviceLocator<GroupServices>();
List<V2TimGroupApplication> groupApplicationList = [];
List<ApplicationStatus> applicationStatusList = [];
@override
void initState() {
super.initState();
groupApplicationList = model.groupApplicationList.where((item) => (item.groupID == widget.groupID)).toList();
applicationStatusList = groupApplicationList.map((item) => ApplicationStatus.none).toList();
}
GroupApplicationItemBuilder _getItemBuilder() {
return widget.itemBuilder ?? _defaultItemBuilder;
}
Widget _defaultItemBuilder(BuildContext context, V2TimGroupApplication applicationInfo, int index) {
final theme = Provider.of<TUIThemeViewModel>(context).theme;
final ApplicationStatus currentStatus = applicationStatusList[index];
String _getUserName() {
if (applicationInfo.fromUserNickName != null &&
applicationInfo.fromUserNickName!.isNotEmpty &&
applicationInfo.fromUserNickName != applicationInfo.fromUser) {
return "${applicationInfo.fromUserNickName} (${applicationInfo.fromUser})";
} else {
return "${applicationInfo.fromUser}";
}
}
String _getRequestMessage() {
String option2 = applicationInfo.requestMsg ?? "";
return TIM_t_para("验证消息: {{option2}}", "验证消息: $option2")(option2: option2);
}
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? const Color(0xFFDBDBDB))),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 12),
child: SizedBox(
height: 40,
width: 40,
child: Avatar(
faceUrl: applicationInfo.fromUserFaceUrl ?? "",
showName: applicationInfo.fromUserNickName ?? applicationInfo.fromUser ?? ""),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_getUserName(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: theme.darkTextColor),
),
Text(
_getRequestMessage(),
style: TextStyle(fontSize: 15, color: theme.weakTextColor),
),
],
)),
if (currentStatus == ApplicationStatus.none && applicationInfo.handleStatus == 0)
Container(
margin: const EdgeInsets.only(left: 8, right: 8),
child: InkWell(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: theme.primaryColor,
border: Border.all(width: 1, color: theme.weakTextColor ?? CommonColor.weakTextColor)),
child: Text(
TIM_t("同意"), // agree
style: const TextStyle(
color: Colors.white,
),
),
),
onTap: () async {
final res = await _groupServices.acceptGroupApplication(
groupID: applicationInfo.groupID,
fromUser: applicationInfo.fromUser!,
toUser: applicationInfo.toUser!,
type: applicationInfo.type,
addTime: applicationInfo.addTime ?? 0,
);
if (res.code == 0) {
setState(() {
applicationStatusList[index] = ApplicationStatus.accept;
});
Future.delayed(const Duration(seconds: 1), () {
model.refreshGroupApplicationList();
});
}
}),
),
if (currentStatus == ApplicationStatus.none && applicationInfo.handleStatus == 0)
InkWell(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.white,
border: Border.all(width: 1, color: theme.weakTextColor ?? CommonColor.weakTextColor)),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
TIM_t("拒绝"), // reject
style: TextStyle(
color: theme.primaryColor,
),
),
),
onTap: () async {
final res = await _groupServices.refuseGroupApplication(
addTime: applicationInfo.addTime!,
groupID: applicationInfo.groupID,
fromUser: applicationInfo.fromUser!,
toUser: applicationInfo.toUser!,
type: GroupApplicationTypeEnum.values[applicationInfo.type]);
if (res.code == 0) {
setState(() {
applicationStatusList[index] = ApplicationStatus.reject;
});
Future.delayed(const Duration(seconds: 1), () {
model.refreshGroupApplicationList();
});
}
},
),
if (currentStatus == ApplicationStatus.accept || applicationInfo.handleResult == 1)
Container(
margin: const EdgeInsets.only(left: 8),
child: Text(
TIM_t("已同意"),
style: TextStyle(fontSize: 15, color: theme.weakTextColor),
),
),
if (currentStatus == ApplicationStatus.reject || applicationInfo.handleResult == 2)
Container(
margin: const EdgeInsets.only(left: 8),
child: Text(
TIM_t("已拒绝"),
style: TextStyle(fontSize: 15, color: theme.weakTextColor),
),
)
],
),
);
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: model)],
builder: (context, w) {
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return Container(
decoration: isDesktopScreen ? null : BoxDecoration(color: theme.weakBackgroundColor),
child: ListView.builder(
shrinkWrap: true,
itemCount: groupApplicationList.length,
itemBuilder: (context, index) {
final applicationInfo = groupApplicationList[index];
final itemBuilder = _getItemBuilder();
return itemBuilder(context, applicationInfo, index);
},
),
);
},
);
}
}
@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application.dart'
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_application.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
enum ApplicationStatus {
none,
accept,
refuse,
}
class TIMUIKitGroupApplicationListItem extends StatefulWidget {
final V2TimGroupApplication applicationInfo;
const TIMUIKitGroupApplicationListItem({Key? key, required this.applicationInfo}) : super(key: key);
@override
State<StatefulWidget> createState() => TIMUIKitGroupApplicationListItemState();
}
class TIMUIKitGroupApplicationListItemState extends TIMUIKitState<TIMUIKitGroupApplicationListItem> {
ApplicationStatus applicationStatus = ApplicationStatus.none;
String _getUserName() {
if (widget.applicationInfo.fromUserNickName != null &&
widget.applicationInfo.fromUserNickName!.isNotEmpty &&
widget.applicationInfo.fromUserNickName != widget.applicationInfo.fromUser) {
return "${widget.applicationInfo.fromUserNickName} (${widget.applicationInfo.fromUser})";
} else {
return "${widget.applicationInfo.fromUser}";
}
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
return Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(color: Colors.white),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 12),
child: SizedBox(
height: 40,
width: 40,
child: Avatar(
faceUrl: widget.applicationInfo.fromUserFaceUrl ?? "",
showName: widget.applicationInfo.fromUserNickName ?? widget.applicationInfo.fromUser ?? ""),
),
),
Column(
children: [
Text(
_getUserName(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: theme.darkTextColor),
),
Text(
_getUserName(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: theme.darkTextColor),
),
],
)
],
),
);
}
}